现在,考虑客户可能会这样使用这个函数:
GUIObject *pgo; // make pgo point to ... // some GUIObject
const Point *pUpperLeft = // get a ptr to the upper &(boundingBox(*pgo).upperLeft()); // left point of its // bounding box | 对 boundingBox 的调用会返回一个新建的临时的 Rectangle 对象。这个对象没有名字,所以我们就称它为 temp。于是 upperLeft 就在 temp 上被调用,这个调用返回一个引向 temp 的一个内部构件的引用,特别是,它是由 Points 构成的。随后 pUpperLeft 指向这个 Point 对象。到此为止,一切正常,但是我们无法继续了,因为在这个语句的末尾,boundingBox 的返回值—— temp ——被销毁了,这将间接导致 temp 的 Points 的析构。接下来,剩下 pUpperLeft 指向一个已经不再存在的对象;pUpperLeft 空悬在创建它的语句的末尾!
这就是为什么任何返回一个对象的内部构件的句柄的函数都是危险的。它与那个句柄是指针,引用,还是迭代器没什么关系。它与是否受到 cosnt 的限制没什么关系。它与那个成员函数返回的句柄本身是否是 const 没什么关系。全部的问题在于一个句柄被返回了,因为一旦这样做了,你就面临着这个句柄比它引用的对象更长寿的风险。
这并不意味着你永远不应该让一个成员函数返回一个句柄。有时你必须如此。例如,operator[] 允许你从 string 和 vector 中取出单独的元素,而这些 operator[]s 就是通过返回引向容器中的数据的引用来工作的——当容器本身被销毁,数据也将销毁。尽管如此,这样的函数属于特例,而不是惯例。
Things to Remember
·避免返回对象内部构件的句柄(引用,指针,或迭代器)。这样会提高封装性,帮助 const 成员函数产生 cosnt 效果,并将空悬句柄产生的可能性降到最低。 |