Item 28:不要返回对象内部的句柄
不要返回对象私有成员的句柄
句柄(handle)可以理解为持有其它对象的方法,引用,指针,和迭代器都是句柄。
不要返回对象私有成员的句柄。这样可以增加类的封装性、使得 const 函数更加 const, 也避免了空引用的创建。
直接返回私有成员的指针会导致私有成员被完全暴露。例如:
class Point { // class for representing points
public:
Point(int x, int y);
...
void setX(int newVal);
void setY(int newVal);
...
};
struct RectData { // Point data for a Rectangle
Point ulhc; // ulhc = " upper left-hand corner"
Point lrhc; // lrhc = " lower right-hand corner"
};
class Rectangle {
...
Point& upperLeft() const { return pData->ulhc; }
Point& lowerRight() const { return pData->lrhc; }
...
private:
std::tr1::shared_ptr<RectData> pData; // see Item 13 for info on
};
一方面,upperLeft 和 lowerRight 是被声明为 const 的成员函数,因为它们被设计成仅仅给客户提供一个获得 Rectangle 的点的方法,而不允许客户改变这个 Rectangle。
另一方面,两个函数都返回引向私有的内部数据的引用——调用者可以利用这些引用修改内部数据!例如:
Point coord1(0, 0);
Point coord2(100, 100);
const Rectangle rec(coord1, coord2); // (0, 0) to (100, 100)
rec.upperLeft().setX(50); // (50, 0) to (100, 100)
在当前情况下,虽然 ulhc 和 lrhc 被声明为 private,它们还是被有效地公开了,因为 public 函数 upperLeft 和 lowerRight 返回了引向它们的引用。
将 const 用于它们的返回类型
class Rectangle {
public:
...
const Point& upperLeft() const { return pData->ulhc; }
const Point& lowerRight() const { return pData->lrhc; }
...
};
通过这个修改的设计,客户可以读取定义一个矩形的 Points,但他们不能写它们。
虽然如此,upperLeft 和 lowerRight 仍然返回一个对象内部构件的句柄,而这有可能造成其它方面的问题。特别是,这会导致空悬句柄:引用了不再存在的对象的构件的句柄。这种消失的对象的最普通的来源就是函数返回值。
考虑一个函数,返回在一个矩形窗体中的 GUI 对象的 bounding box:
class GUIObject { ... };
const Rectangle boundingBox(const GUIObject& obj);
考虑客户可能会这样使用这个函数:
GUIObject *pgo;
const Point *pUpperLeft = &(boundingBox(*pgo).upperLeft());
对 boundingBox 的调用会返回一个新建的临时的 Rectangle 对象。这个对象没有名字,所以我们就称它为 temp。于是 upperLeft 就在 temp 上被调用,这个调用返回一个引向 temp 的一个内部构件的引用,特别是,它是由 Points 构成的。随后 pUpperLeft 指向这个 Point 对象。
到此为止,一切正常,但是我们无法继续了,因为在这个语句的末尾,boundingBox 的返回值—— temp ——被销毁了,这将间接导致 temp 的 Points 的析构。接下来,剩下 pUpperLeft 指向一个已经不再存在的对象;pUpperLeft 空悬在创建它的语句的末尾!
总结
- 句柄(handle)可以理解为持有其它对象的方法,引用,指针,和迭代器都是句柄。
- 避免返回对象内部构件的句柄。这样会提高封装性,帮助 const 成员函数产生 cosnt 效果,并将空悬句柄产生的可能性降到最低。