我们举个例子来看这个条款面临的问题
设计一个表示矩形的类,为了让这个类尽量的小,可以不把表示矩形的点放在这个类中,而是放在一个struct中,在类中指向它。
class Point { //表示点的类
public:
Point(int x, int y);
.....
void setX(int newVal);
void setY(INT newVal);
};
struct RectData { //这些点数据用来表现一个矩形
Point ulhc; //upper left hand corner
Point lrhc; //lower right hand corner
};
class Rectangle {
public:
Point& upperLeft() const {return pData->ulhc; }
Point& lowerRight() const {return pData->lrhc; }
private:
std::trl::shared_ptr<RectData> pData; //指向了结构体
};
上面这样的设计的问题:在类Rectangle中我们提供upperLeft和lowerRight函数只是为了返回矩形的范围的,所以我们声明为const。可是由于它返回reference指向private内部数据,调用者于是可通过这些references更改内部数据。例如:
Point coord1(0.0);
Point coord1(100.100);
const Rectangle rec(coord1, coord2); //rec是不可改变的矩形
rec.upperLeft().setX(50); //通过返回的reference修改了矩形,这是和我们的设计初衷不符的
造成上面问题的根本原因是:类的成员函数返回了指向类的私有成员的reference。如果它们返回的是指针或迭代器,情况是一样的。Reference,指针和迭代器都是所谓的handles。因些应该避免类的成员函数返回这样的handles。
解决方法:
声明其返回一个静态的reference。例:
class Rectangle {
public:
const Point& upperLeft() const {return pData->ulhc; }
const Point& lowerRight() const {return pData->lrhc; }
private:
std::trl::shared_ptr<RectData> pData; //指向了结构体
};
这样的reference只允许你读取,不可以修改。
上面的问题结决了,可是upperLeft和lowerRight还是返回了指向内部对象的handles。这还会出现一个问题就是dangling handles。就是说这种handles所指向东西不复存在,它通常发生在函数返回值时。
例如:某个函数返回GUI对象的外框(bounding box),这个外框采用矩形形式:
class GUIObject {....};
const Rectangle boundingBox(const GUIObject& obj);//以by value返回一个矩形
客户可以使用这个函数,如:
GUIObject* pgo; //以pgo指向某个GUIObject
....
const Point* pUpperLeft = &(boundingBox(*pgo).UpperLeft()); //取得一个指针指向外框左上点
问题出在boundingBox(*pgo)获得的是一个临时的Rectangle对象,这个语句之后这个临时的对象就被销毁了,可是UpperLeft返回了一个Reference指向一个Points。这导致了返回的Reference指向了一个空的对象。所以要避免类的成员函数返回这样的handles
总结:
避免返回handles(包括references,指针,迭代器)指向对象内部。遵守这个条款增加封装性,帮助const成员函数的行为像个const,并将发生”虚吊号码牌“dangling handles 的可能性降至最低
Effective C++读书笔记之避免返回handles指向对象内部成分
最新推荐文章于 2019-11-08 17:19:21 发布