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 效果,并将空悬句柄产生的可能性降到最低。
posted @ 2020-02-09 21:55  刘-皇叔  阅读(149)  评论(0编辑  收藏  举报