《Effective C++》学习笔记——条款28
***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************
五、Implementations
Rule 28:Avoid returning "handles" to object internals
规则 28:避免返回handles指向对象内部成分
假设我们的程序涉及矩形。每个矩形由其左上角和右下角表示。为了让一个Rectangle对象尽可能小,你可能会决定不把定义矩形的这些点放在Rectangle对象内,而是放在一个辅助的struct内再让Rectangle去指它:
class Point { // 描述点 的类 public: Point( int x ,int y ); … void setX(intnewVal); void setY(intnewVal); … }; struct RectData { //用这些”点”数据用来表现一个矩形 Point ulhc; // 左上角 Point lrhc; // 右下角 }; class Rectangle { … private: std::tr1::shared_ptr<RectData> pData; };
为了计算Rectangle的范围,这个类提供upperLeft函数 和 lowerRight函数,因为Point是用户自定义函数,所以这些函数是返回reference,代表底层的Pointer对象:
class Rectangle { public: … Point&upperLeft() const { returnpData->ulhc; } Point&lowerRight() const { returnpData->lrhc; } … };
虽然这种设计可以通过编译,但是它是错误的。实际上它是自我矛盾的——
①upperLeft和lowerRight被声明为const成员函数,因为它们的目的只是为了提供客户一个得知Rectangle相关坐标点的方法,而不是让客户修改Rectangle;
②两个函数却都返回reference指向private内部数据,调用者于是可通过这些 reference 更改内部数据。
一个明显的例子:
Point coord1(0,0); Point coord2(100,100); const Rectangle rec(coord1,coord2); // 定义矩阵 rec,左上角(0,0),右下角(100,100) rec.upperLeft().setX(50); // 将rec变成 左上角(50,0),右下角(100,100)
显然,upperLeft的调用者能够使用被返回的reference来更改成员。但rec实际上应该是不可变的。
上面这个例子,告诉我们两点:
? 成员变量的封装性最多只等于”返回其reference“的函数的访问级别。
? 如果const成员函数传出一个reference,后者所指数据与对象自身有关联,而它又被存储于对象之外,那么这个函数的调用者可以修改那笔数据。这正是bitwise constness的一个附带结果。
我们上面说的每件事情都是由于“成员函数返回reference”。如果它们返回的是指针或迭代器,相同的情况还是会发生,原因也相同——reference、指针 和 迭代器统统都是所谓的 handles,而返回一个“代表对象内部数据”的handle,随之而来的便是“降低对象封装性”的风险。
PS:我们通常认为的对象的“内部”就是指它的成员变量,但其实不被公开使用的成员函数(也就是 protected 或 private)也是对象“内部”的一部分。
解法:“返回指针指向某个成员函数”的情况并不多见,所以让我们把注意力收回,专注于Rectangle class和它的 upperLeft以及lowerRight成员函数。我们在这些函数身上遭遇的两个问题可以轻松去除,只要对它们的返回类型加上const即可:
class Rectangle { public: … const Point& upperLeft() const { return pData->ulhc; } const Point& lowerRight() const { return pData->lrhc; } … };
<span style="font-family:KaiTi_GB2312;font-size:14px;">class GUIObject { ... }; const Rectangle boundingBox(const GUIObject& obj ); // 通过by value方式返回一个矩形 </span>
<span style="font-family:KaiTi_GB2312;font-size:14px;">GUIObject* pgo; // 让pgo指向某个GUIObject ... const Point* pUpperLeft = &(boundingBox(*pgo).upperLeft()); // 取得一个指针指向外框左上点</span>
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。