Effective C++:条款28:避免返回 handles 指向对象内部成员
(一)
有时候为了让一个对象尽量小,能够把数据放在另外一个辅助的struct中,然后再让一个类去指向它。看以下的代码:
class Point { public: Point(int x, int y); void setX(int newVal); void setY(int newVal); }; struct RectData { Point ulhc; Point lrhc; }; class Rectangle { public: Point& upperLeft() const { return pData->ulhc; } Point& lowerRight() const { return pData->lrhc; } private: std::tr1::shared_ptr<RcetData> pData; };这种设计看上去非常beautiful。可是却是错误的,实际上它是自相矛盾的,看以下的代码:
Point coord1(0, 0); Point coord2(100, 100); const Rectangle rec(coord1, coord2); rec.upperleft().setX(50);错误的理由:(1)upperLeft()跟lowerRight()这两个函数都是const的,所以客户不能改动Rectangle;
(2)两个函数都返回reference指向private内部数据,调用者于是可通过这些reference更改内部数据。
upperLeft的调用者可以使用被返回的引用来更改成员。但rec事实上应该是不可变的(const)!
所以上面那种类的设计是错误的!!!
所以从这个样例中,我们能够得到下面的教训:
(1)成员变量的封装性会被引用破坏。
(2)假设const成员函数传出一个reference,后者所指的数据与对象自身有关联,而它又被存储于对象之外,那么这个函数的调用者能够改动那笔数据。
相同的道理,返回对象的引用、指针、迭代器都会造成这样的局面,它们都是“句柄”。返回一个代表对象内部数据的句柄,会减少对象的封装。
(二)解决的方法:
仅仅要对这两个函数的返回类型加上const就可以:
class Rectangle { public: const Point& upperLeft() const { return pData->ulhc; } const Point& lowerRight() const { return pData->lrhc; } private: std::tr1::shared_ptr<RcetData> pData; };有了这种改变,客户就仅仅能读取矩形的Points,可是不能涂写它们。
(三)
上面那种解决方法尽管确保了内部对象不会被改动。可是却可能导致dangling handles(空悬的号码牌):这样的handles所指东西(的所属对象)不复存在。
这样的“不复存在的对象”最常见的来源就是函数返回值。
class GUIObject {...}; const Rectangle boundingBox(const GUIObject& obj); GUIObject* pgo; const Point* pUpperLeft = &(boundingBox(*pgo).upperLeft());你会发现(boundingBox(*pgo).upperLeft())这是一个point对象。可是当这一句运行完后,这个暂时对象temp会被析构。这时,pUpperLeft会指向一个空的对象。也就出现了悬空现象。
因此,这就是为什么函数假设“返回一个handle代表对象内部成分“总是危急的原因。
请记住:
(1)避免返回handles指向对象的内部。遵守这个条款可添加封装性,帮助const成员函数更加像一个const,并将“虚号码牌“的可能性减少到最低。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。