私有对象指针vs对象值,以及返回对象内部

时间:2010-06-26 22:39:49

标签: c++ pointers encapsulation

相关:C++ private pointer "leaking"?

根据Effective C ++(第28项),“避免将句柄(引用,指针或迭代器)返回到对象内部。它增加了封装,帮助const成员函数执行const,并最小化悬空句柄的创建。”

按值返回对象是我能想到避免返回句柄的唯一方法。这对我来说建议我应该尽可能多地返回私有对象内部。

但是,要按值返回对象,这需要复制构造函数,该构造函数与“DISALLOW_COPY_AND_ASSIGN”运算符的Google C++ Style Guide相对。

作为一个C ++新手,除非我遗漏了什么,否则我发现这两个建议相互冲突。

所以我的问题是:是否没有银弹允许有效的参考返回到不易受悬空指针影响的对象内部? const引用的返回是否与它一样好?另外,我应该经常使用私有对象字段的指针吗?选择何时按值或通过指针存储对象的私有实例字段的一般经验法则是什么?

(编辑)为了澄清,Meyers的示例悬空指针代码:

class Rectangle {
public:
  const Point& upperLeft() const { return pData->ulhc; }
  const Point& lowerRight() const { return pData->lrhc; }
  ...
};

class GUIObject { ... };
const Rectangle boundingBox(const GUIObject& obj); 

如果客户端使用以下代码创建函数:

GUIObject *pgo; // point to some GUIObject
const Point *pUpperLeft = &(boundingBox(*pgo).upperLeft());
  

“对boundingBox的调用将返回一个新的临时Rectangle对象[(此处称为temp。)] upperLeft将在temp上调用,该调用将返回对temp内部部分的引用,特别是在其中一个点构成它...在语句结束时,boundingBox的返回值temp将被销毁,这将间接导致temp点的破坏。这反过来会使pUpperLeft指向一个对象不再存在。“ Meyers,Effective C ++(Item 28)

我认为他建议按值返回Point以避免这种情况:

const Point upperLeft() const { return pData->ulhc; }

3 个答案:

答案 0 :(得分:5)

Google C ++风格指南应该说有些“特殊”,并引发了对各种C ++新闻组的大量讨论。让我们离开吧。

在正常情况下,我建议遵循有效C ++中的指南通常被认为是一件好事;在您的特定情况下,返回一个对象而不是对内部对象的任何类型的引用通常是正确的做法。大多数编译器都非常擅长处理大的返回值(谷歌的返回值优化,几乎每个编译器都这样做。)

如果使用分析器进行测量表明返回值正在成为瓶颈,那么我会考虑其他方法。

答案 1 :(得分:2)

首先,让我们看看上下文中的这个陈述:

  

根据Effective C ++(第28项),   “避免返回句柄(参考文献,   对象的指针或迭代器   内部。它增加了封装,   帮助const成员函数行动   const,并最小化的创建   晃来晃去的手柄。“

这基本上是在谈论一个类维护不变量的能力(粗略地讲,这些属性保持不变)。

假设您有一个按钮窗口小部件包装器按钮,它存储按钮的特定于操作系统的窗口句柄。如果使用该类的客户端可以访问内部句柄,他们可以使用特定于操作系统的调用来篡改它,例如销毁按钮,使其不可见等。基本上通过返回此句柄,您的 Button 类牺牲它最初对按钮手柄的控制。

您希望通过提供您可以使用按钮作为此Button类中的方法所做的一切来避免在这样的Button类中出现这些情况。然后,您无需返回特定于操作系统的按钮句柄的句柄。

不幸的是,这在实践中并不总是有效。有时您必须出于各种原因返回句柄或指针或其他内部参考。举个例子,我们来看看 boost :: scoped_ptr 。它是一个智能指针,旨在通过它存储的内部指针来管理内存。它有一个get()方法返回这个内部指针。不幸的是,这允许客户做以下事情:

delete my_scoped_ptr.get(); // wrong

然而,这种妥协是必需的,因为在很多情况下我们正在使用需要传入常规指针的C / C ++ API。通常需要妥协以满足不接受您的特定类但接受的库其中一个内部。

在您的情况下,尝试考虑您的类是否可以通过这种方式避免返回内部,而是通过公共接口提供函数来执行内部操作。如果没有,那么你已经做了所有你能做的事情;你将不得不返回一个指针/引用,但将它作为特例记录是一个好习惯。如果您知道哪些地方需要事先获得班级内部的访问权限,您还应该考虑使用朋友;通过这种方式,您可以将此类访问器方法保密,并且不会被其他人访问。

  

按值返回对象是唯一的   我可以想到避免回归的方式   处理。对我来说这表明我应该   返回私有对象内部   价值尽可能多。

不,如果您可以返回副本,那么您可以通过const引用同样返回。客户不能(在正常情况下)篡改这种内部。

答案 2 :(得分:0)

这实际上取决于具体情况。如果您打算在调用方法中看到要通过引用传递的更改。请记住,通过值传递是一项非常繁重的操作。它需要调用复制构造函数,实际上它必须分配和存储足够的内存以适合对象的大小。

你可以做的一件事就是伪造价值。这意味着将值的实际参数传递给接受const对象的方法。这当然意味着调用者不关心看到对象的更改。

除非必须,否则尽量限制传递值。