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

1、什么是handles?函数返回handles有什么危害?

reference、指针、迭代器系统都是所谓的handles(号码牌,用来获得某个对象)。函数返回一个handle,随之而来的便是“减低对象封装性”的风险。它也可能导致:虽调用const成员函数却造成对象状态被更改的风险。

2、返回handles 指向对象内部成分 可能带来的问题一:自相矛盾

class Point{
public:
       Point(intx,inty);
       voidsetX(intnewVal);
       voidsetY(intnewVal);
};

struct RectData{
       Pointulhc;
       Pointlrhc;
};

class Rectangle{
public:
       Point&upperLeft()const{returnpData->ulhc;}
       Point&lowerRight()const{returnpData->lrhc;}
       ...
private:
       std::tr1::shared_ptr<RectData>pData;
};

上述代码便是自相矛盾的一个例子。point 类是一个代表点的类,RectData代表一个矩形的结构,Rectangle类则代表一个矩形,该类能够返回表示矩阵的左上和右下的两个点。由于这两个函数为const的,因此所要表达的意思就是,返回矩阵的两个点,但是不能修改他们。但是又又于返回的是点的reference形式,因此通过reference,实际是可以改变返回的点的数据的。因此,造成了自相矛盾。问题的原因就是,函数返回了handle。

3、上述矛盾带来的启示

  • 成员变量的封装性最多等于“返回其reference”的函数的访问级别。即使数据本身被声明为private的,但是如果返回他们的reference是public的,那么数据的访问权限就编程public了。
  • 如果const成员函数传出一个reference,后者所指数据又不在自身对象内,那么这个函数的调用者可以修改此数据。(这是 bitwise constness 带来的后果。)

4、改进版 返回const handles 指向对象内部成分 可能带来的问题:解决了自相矛盾,却可能形成虚吊问题

(1)上述代码的改进版本:在返回handles 的成员函数前加const。这便解决了自相矛盾问题。

class Rectangle{
public:
       const Point&upperLeft()const{returnpData->ulhc;}
       const Point&lowerRight()const{returnpData->lrhc;}
       ...
private:
       std::tr1::shared_ptr<RectData>pData;
};

(2)上述代码在其他场景下可能存在的问题:虚吊问题
所谓虚吊问题,就是指针指向了一个不复存在的对象。最常见的问题来源就是函数返回值。

例如,某个函数返回GUI对象的外框,是一个矩形形式

	class GUIObject{};
	const Rectangle boundingBox(constGUIObject&obj);
	//现在,客户可能这么使用。
	GUIObject *pgo;
	const Point *pUpperLeft=&( boundingBox(*pgo).upperLeft() );

boundingBox 函数传入一个GUI对象,它返回一个GUI的外框,即是一个矩形,然后获取这个去腥的右下方的点,并使用一个指针指向它。而函数的返回值是一个临时的对象,即这个矩形是一个临时的对象,当这个语句执行结束后,矩形对象被销毁,因此其内部的点也被销毁,而此时pUpperLeft指向了一个被销毁的点。就形成了所谓的虚吊。

4、最终的结论

无论返回的handle是指针、reference、或者迭代器,也无论他是否为const。只要一个handle被传出去了,都是比较危险的。

5、本条款的例外情况

有时候必须返回handle,例如operator[],operator=。然而着只是少数的例外。

posted @ 2020-01-07 21:09  江南又一春  阅读(197)  评论(0编辑  收藏  举报