首先看看下面这个例子:
1 class Point{ 2 public: 3 point(int x, int y); 4 ... 5 void setX(int newVal); 6 void setY(int newVal); 7 ... 8 }; 9 struct RectData{ 10 Point ulhc; //左上角 11 Point lrhc; //右下角 12 }; 13 class Rectangle{ 14 ... 15 private: 16 shared_ptr<RectData> pData; 17 };
那么如果出现下面这种成员函数的话:
1 class Rectangle{ 2 public: 3 ... 4 Point & upperLeft() const {return pData->ulhc; } 5 Point & lowerRight() const{return pData->lrhc; } 6 ... 7 }
这样只是能通过编译,但是设计确是错误的,在成员函数被声明为const的情况下返回了一个内部成员的引用,这样使得ulhc 以及 lrhc对象都可以被更改。但是二者都是private的,实际上二者都是不应该被改变的。
这正是由于返回的是引用,同样的返回指针,迭代其这种叫做handle的对象都是会造成内部状态暴露在外面。
这些问题实际上在每个方法的返回的handle上加上const就会得到解决。
1 class Rectangle{ 2 public: 3 ... 4 const Point & upperLeft() const {return pData->ulhc; } 5 const Point & lowerRight() const{return pData->lrhc; } 6 ... 7 }
但是几十这样做仍然可能会导致造成虚调handle的现象发生:
class GUIobject{...}; const Rectangle boundingBox(const GUIobject & obj); //那么,当用户这样去使用他的时候 GUIobject * pgo; ... const Point * pUpperLeft = &(boundingBox(*pgo).pUpperLeft());
这样实际上将Rectancle中的Point对象传递出去了,这样当临时对象析构的时候,这个指针就会编程一个空悬的指针(感觉通过智能指针就能解决啊),这样也带来了风险。
小结:
避免返回Handle指向内部对象,这样可以增加封装性,帮助const函数的行为像是一个const,并将空悬Handle的可能性降到最低。