条款28:避免返回handles指向对象内部成分

来源:互联网 发布:西门子s71500编程手册 编辑:程序博客网 时间:2024/05/16 02:53

首先说明,在第三版中,这一条款有两处明显的错误:

第一是在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 就成了一个悬空句柄(不代表任何实际存在的对象)。由此可见,返回一个指向对象内部成分的句柄,是一项危险的事情,因为对象可能在任何时候被析构,此后,这个句柄就成了悬空句柄了。


总而言之,避免返回指向对象内部的句柄。首先,这样会提高类的封装性;其次,这样可以避免悬空句柄的出现。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 华为手机一直显示开机画面怎么办 华为p7手机开不了机怎么办 华为荣耀8弯了怎么办 手机壳掉漆了怎么办 华为5a手机音量小怎么办 华为5a手机声音小怎么办 苹果屏幕磨花了怎么办 白色磨砂手机壳脏了怎么办 胶皮手机壳变黄怎么办 手机壳边缘黑了怎么办 手机壳磨黑了怎么办 iphon8原装后壳碎裂怎么办 皮的手机壳发黄怎么办 荣耀手机一直在开机画面怎么办 玻璃手机壳碎了怎么办 华为6x信号不好怎么办 昂达平板v819i刷成砖了怎么办 华为5x忘记密码怎么办 荣耀8开不了机怎么办 华为5s死机了怎么办 华为重启后忘了解锁密码怎么办 华为mate.9上网速度慢怎么办 华为mate10上网速度慢怎么办 四核豌豆2变砖怎么办 苹果32g不够用怎么办 移动4g网络卡怎么办 移动4g网非常卡怎么办 移动4g卡网速慢怎么办 手机移动4g网卡怎么办 华为手机摄像头进灰怎么办 美图t8忘记密码怎么办 华为mate7忘记开机密码怎么办 华为mate9开机密码忘记怎么办 华为手机系统更新失败怎么办 mate9系统升级后耗电快怎么办 华为荣耀手机耗电快怎么办 荣耀10手机拍照不清晰怎么办 荣耀6x拍照模糊怎么办 华为荣耀P9进水了怎么办 苹果5s手机信号不好怎么办 苹果5s信号不好怎么办?