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

假设程序涉及矩形。为了让Rectangle对象尽可能小,可能把定义矩形的点放在一个辅助的struct内再让Rectangle去指它:

class Point{
public:
    Point(int x, int y);
    ...
    void setX(int newVal);
    void setY(int newVal);
    ...
};
struct RectData{
    Point ulhc;
    Point lrhc;
};
class Rectangle{
    ...
    Point& upperLeft()const {return pData->ulhc;}
    Point& lowerRight()const {return pData->lrhc;}
private:
    std::tr1::shared_ptr<RectData> pData;
};

这样的设计可以通过编译,但却是错误的。实际上自相矛盾,一方面upperleft和lowerRight被声明为const,不让客户修改Rectangle。另一方面,这两个函数都返回reference指向private数据,调用者可以通过这些reference更改内部数据:

Point coord1(0,0);
Point coord2(100,100);
const Rectangle rec(coord1, coord2);
rec.upperLeft().setX(50);//现在rec变成从(50,0)到(100,100)

第一,成员变量的封装性最多只等于“返回其reference”的函数的访问级别

第二,如果const成员函数传出一个reference,后者所指数据与对象自身有关,而它又被存储在对象之外,那么这个函数的调用者可以修改那笔数据。这正是bitwise constness的一个附带结果,条款3。

如果它们返回的是指针或迭代器,相同的结果还会发生,原因相同。reference、指针和迭代器统统都是所谓的handles(号码牌,用来取得某个对象),而返回一个“代表对象内部数据的handle”,随之而来的便是“降低对象封装性”的风险。同时,也可能造成“虽然调用const成员函数却造成对象状态被更改”。

通常我们认为,对象的“内部”就是指它的成员变量,其实不被公开使用的成员函数(protected或private)也是对象“内部”的一部分,所以也不该返回它们的handles。否则,它们的访问级别就会提高到返回它们的成员函数的访问级别。

上述两个问题可以在它们的返回类型上加上const即可:

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

const 不在是个谎言。至于封装性,这里是蓄意放松封装,有限度的放松:只让渡读取权,涂写权是禁止的。

即使这样,返回“代表对象内部”的handles,有可能在其他场合导致dangling handles(空悬的号码牌):

这种handle所指东西(的所属对象)不复存在。这种“不复存在的对象”最常见的来源就是函数返回值。

class GUIObject{...};
const Rectangle boundingBox(const GUIObject& obj);

现在,客户可能这么使用这个函数:

GUIObject *pgo;
...
const Point* pUpperLeft = &(boundingBox(*pgo).upperLeft());//取得一个指针指向外框左上点

对boundingBox的调用获得一个新的、暂时的Rectangle对象,这个对象没有名称,权且称它为temp。随后upperLeft作用于temp对象身上,返回reference指向temp的一个内部成分。具体指向temp的那个Point对象。但是这个语句结束之后,boundingBox的返回值,也就是我们所说的temp,将被销毁,而那间接导致temp内的Points析构。最终导致pUpperLeft指向一个不再存在的对象;变成空悬、虚吊(dangling)!

只要handle被传出去了,不管这个handle是不是const,也不论返回handle的函数是不是const。这里的唯一关键是暴露在“handle比其所指对象更长寿”的风险下。

原文地址:https://www.cnblogs.com/lidan/p/2335935.html