文章目录
我们来定义一个矩形类:
class Point{ //“点”类
public:
point(int x,int y);
...
void setX(int newVal);
void setY(int newVal);
...
};
struct RectData{ //矩形的结构
Point ulhc; //左上角的点
Point lrhc; //右下角的点
};
class Rectangle{
...
private:
std::tr1::shared_ptr<RectData> pData;
};
Rectangle的客户需要计算Rectangle的范围,所以我们要定义函数返回成员变量:
class Rectangle{
public:
...
Point& upperLeft() const{return pData->ulhc;}
Point& lowerRight() const{return pData->lrhc;}
...
};
上面的设计似乎符合预期,但是调用者可通过reference更改内部数据:
Point coord1(0,0);
Point coord2(100,100);
const Rectangle rec(coord1,coord2); //rec是个const矩形,从(0,0)到(100,100)
rec.upperLeft().setX(50); //现在rec却变成从(50,0)到(100,100)
上面出现修改rec的值,由此带给我们两个教训:
(1)成员变量的封装性最多只等于“返回其reference”的函数的访问级别。
(2)如果const成员函数传出一个reference,后者所指数据与对象自身有关联,而它又被存储于对象之外,那么这个函数的调用者可以修改那笔数据。
解决以上问题,我们只要对它们的返回类型加上const即可:
class Rectangle{
public:
...
const Point& upperLeft() const { return pData->ulhc; }
const Point& lowerRight() const { return pData->lrhc; }
...
};
上面的改动已经保证了函数调用者不可以修改那笔数据,但即使如此,它可能导致dangling handle(空悬的号码牌):这种handles所指东西不复存在。这种情况我们容易理解,这里不再赘述。
总结——156
避免返回handles(包括reference、指针、迭代器)指向对象内部。遵守这个条款可增加封装性,帮助const成员函数的行为像个const,并将发生“虚吊号码牌”(dangling handles)的可能性将至最低。