条款28:避免返回handles指向对象内部成分
2012-09-12 09:52
495 查看
首先说明,在第三版中,这一条款有两处明显的错误:
第一是在124页下面:这立刻带给我们两个教训:第一,成员变量的封装性最多只等于“返回其引用”的函数的访问级别。本例中虽然ulhc和urhc都被声明为private。
这里明显是错了:c++primer里说过,如果类以struct开头,里面的默认标号就是public;而且,如果不是public,pData->ulhc是不能实现的。这里改为:虽然
ulhc 和 lrhc 被它们的 Rectangle 认为是 private(私有)的
第二是125页下面:这意味着当初声明upperLeft和upperLeft为const不再是个谎言。
这里应该是upperLeft和lowerRight,不过不影响阅读。
言归正传,先看一个例子:
//点类
class Point
{
public:
Point(int xVal, int yVal):x(xVal),y(yVal){}
~Point(){}
void setX(int newX){ x = newX;}
//返回X坐标,以后测试用
int getX()const{return x;}
void setY(int newY){ y = newY;}
private:
int x;
int y;
};
//矩形数据结构
struct RectData
{
RectData(const Point& p1, const Point& p2):ulhc(p1),lrhc(p2){}
Point ulhc;//坐上
Point lrhc;//右下
};
//矩形类
class Rectangle
{
public:
Rectangle(RectData data):pData(new RectData(data)){}
const Point& upperLeft()const{return pData->ulhc;}
const Point& lowerRight()const{return pData->lrhc;}
//private:
std::tr1::shared_ptr<RectData> pData;
};虽然我们声明upperLeft()和lowerRight()都是const函数,但是我们可以通过它来修改矩形的点:
通过这个例子可以看出:
1.变量的封装性最多等于“返回其引用”的函数的访问级别:这里upperLeft函数是返回的都是Point类型的引用,所以即使矩形的数据pData被声明为private,但是还是可以访问里面的内容。
2.如果函数成员返回一个指向数据的引用,那么且这个数据被储存在对象之外,那么即使这个函数被声明为const,我们也可以通过这个函数修改它。在这里,Rectangle类的数据成员只是一个指向RectData的智能指针,而指针实际指向的数据,却是在RectData中储存的。upperLeft虽然声明为const,但这只意味着他不修改指针(的指向),至于指针指向的内容,当然是可以修改的了。
同理,返回对象的引用、指针、迭代器都会造成这种局面,它们都是“句柄”。返回一个代表对象内部数据的句柄,会降低对象的封装。
在这个例子中,只要对它们的返回类型加上const就可以了:
const Point& upperLeft()const{return pData->ulhc;}
const Point& lowerRight()const{return pData->lrhc;}
即使这样,由于upperLeft()函数返回了代表对象内部的句柄,它也会产生其他方面的问题,比如:悬空句柄——这个句柄所指的东西并不存在。举一个例子:
//一个GUI对象
class GUIObject
{
public:
GUIObject(Rectangle r){}
//返回一个指定大小的矩形框
const Rectangle getRec()const
{
Point coord1(50,50);
Point coord2(200,200);
RectData data(coord1,coord2);
const Rectangle rec(data);
return rec;
}
};
//返回obj的外框
const Rectangle boundingBox(const GUIObject& obj)
{
return obj.getRec();
}
下面测试:
Point coord1(10,10);
Point coord2(100,100);
RectData data(coord1,coord2);
const Rectangle rec(data);
GUIObject obj(rec);
//一个GUI对象指针
GUIObject* pgo = &obj;
//获取它的左上角点
const Point* pUpperLeft = &(boundingBox(*pgo).upperLeft());
cout<<pUpperLeft->getX();
return 0;
boundingBox返回一个新的Rectangle对象。然后调用upperLeft函数返回它的左上角。但是,当这条语句结束以后,boundingBox的返回值就会被销毁了,导致它的Points被析构,所以pUpperLeft 就成了一个悬空句柄(不代表任何实际存在的对象)。由此可见,返回一个指向对象内部成分的句柄,是一项危险的事情,因为对象可能在任何时候被析构,此后,这个句柄就成了悬空句柄了。
总而言之,避免返回指向对象内部的句柄。首先,这样会提高类的封装性;其次,这样可以避免悬空句柄的出现。
第一是在124页下面:这立刻带给我们两个教训:第一,成员变量的封装性最多只等于“返回其引用”的函数的访问级别。本例中虽然ulhc和urhc都被声明为private。
这里明显是错了:c++primer里说过,如果类以struct开头,里面的默认标号就是public;而且,如果不是public,pData->ulhc是不能实现的。这里改为:虽然
ulhc 和 lrhc 被它们的 Rectangle 认为是 private(私有)的
第二是125页下面:这意味着当初声明upperLeft和upperLeft为const不再是个谎言。
这里应该是upperLeft和lowerRight,不过不影响阅读。
言归正传,先看一个例子:
//点类
class Point
{
public:
Point(int xVal, int yVal):x(xVal),y(yVal){}
~Point(){}
void setX(int newX){ x = newX;}
//返回X坐标,以后测试用
int getX()const{return x;}
void setY(int newY){ y = newY;}
private:
int x;
int y;
};
//矩形数据结构
struct RectData
{
RectData(const Point& p1, const Point& p2):ulhc(p1),lrhc(p2){}
Point ulhc;//坐上
Point lrhc;//右下
};
//矩形类
class Rectangle
{
public:
Rectangle(RectData data):pData(new RectData(data)){}
const Point& upperLeft()const{return pData->ulhc;}
const Point& lowerRight()const{return pData->lrhc;}
//private:
std::tr1::shared_ptr<RectData> pData;
};虽然我们声明upperLeft()和lowerRight()都是const函数,但是我们可以通过它来修改矩形的点:
int main() { Point coord1(0,0); Point coord2(100,100); RectData data(coord1,coord1); const Rectangle rec(data); rec.upperLeft().setX(50); return 0; }
通过这个例子可以看出:
1.变量的封装性最多等于“返回其引用”的函数的访问级别:这里upperLeft函数是返回的都是Point类型的引用,所以即使矩形的数据pData被声明为private,但是还是可以访问里面的内容。
2.如果函数成员返回一个指向数据的引用,那么且这个数据被储存在对象之外,那么即使这个函数被声明为const,我们也可以通过这个函数修改它。在这里,Rectangle类的数据成员只是一个指向RectData的智能指针,而指针实际指向的数据,却是在RectData中储存的。upperLeft虽然声明为const,但这只意味着他不修改指针(的指向),至于指针指向的内容,当然是可以修改的了。
同理,返回对象的引用、指针、迭代器都会造成这种局面,它们都是“句柄”。返回一个代表对象内部数据的句柄,会降低对象的封装。
在这个例子中,只要对它们的返回类型加上const就可以了:
const Point& upperLeft()const{return pData->ulhc;}
const Point& lowerRight()const{return pData->lrhc;}
即使这样,由于upperLeft()函数返回了代表对象内部的句柄,它也会产生其他方面的问题,比如:悬空句柄——这个句柄所指的东西并不存在。举一个例子:
//一个GUI对象
class GUIObject
{
public:
GUIObject(Rectangle r){}
//返回一个指定大小的矩形框
const Rectangle getRec()const
{
Point coord1(50,50);
Point coord2(200,200);
RectData data(coord1,coord2);
const Rectangle rec(data);
return rec;
}
};
//返回obj的外框
const Rectangle boundingBox(const GUIObject& obj)
{
return obj.getRec();
}
下面测试:
Point coord1(10,10);
Point coord2(100,100);
RectData data(coord1,coord2);
const Rectangle rec(data);
GUIObject obj(rec);
//一个GUI对象指针
GUIObject* pgo = &obj;
//获取它的左上角点
const Point* pUpperLeft = &(boundingBox(*pgo).upperLeft());
cout<<pUpperLeft->getX();
return 0;
boundingBox返回一个新的Rectangle对象。然后调用upperLeft函数返回它的左上角。但是,当这条语句结束以后,boundingBox的返回值就会被销毁了,导致它的Points被析构,所以pUpperLeft 就成了一个悬空句柄(不代表任何实际存在的对象)。由此可见,返回一个指向对象内部成分的句柄,是一项危险的事情,因为对象可能在任何时候被析构,此后,这个句柄就成了悬空句柄了。
总而言之,避免返回指向对象内部的句柄。首先,这样会提高类的封装性;其次,这样可以避免悬空句柄的出现。
相关文章推荐
- 条款28:避免返回handles指向对象内部的成分(Avoid returning "handles" to objects internals)
- 【Effection C++】读书笔记 条款28:避免返回handles指向对象内部成分
- Effective C++ -----条款28:避免返回handles指向对象内部成分
- 《Effect C++》学习------条款28:避免返回handles指向对象内部成分
- 条款28:避免返回handles指向对象内部成分
- 条款28 :避免返回handles指向对象内部成分
- 读书笔记《Effective C++》条款28:避免返回handles指向对象内部成分
- Effective C++ 条款28 避免返回handles指向对象内部成分
- 条款28:避免返回handles指向对象内部成分
- 条款28 避免返回handles指向对象内部成分
- 条款28:避免返回handles指向对象内部成分
- effective C++ 条款 28:避免返回handles指向对象内部成分
- 条款28:避免返回handles指向对象内部成分
- 条款28:避免返回handles指向对象的内部成分。
- 条款28:避免返回handles指向对象内部成分
- 28 避免返回handles指向对象内部成分——effective c++
- Effective C++ (E3 28)笔记之避免返回指向对象内部成分的handles、浅拷贝与深拷贝的探讨
- Effective C++:条款28:避免返回 handles 指向对象内部成员
- Effective C++:条款28:避免返回 handles 指向对象内部成员
- 《Effective C++》学习笔记条款28 避免返回handls指向对象内部成分