2010-06-26 81 views
1

相关:C++ private pointer "leaking"?私有对象指针VS对象值,并返回对象内部构件

根据有效的C++(项目28),“避免返回手柄(引用,指针,或迭代器)到对象内部它增加封装。 ,可以帮助const成员函数执行const,并最大限度地减少悬挂句柄的创建。“

通过值返回对象是我能想到的避免返回句柄的唯一方法。对我来说这意味着我应该尽可能地按照值返回私有对象内部。

但是,要按值返回对象,这需要使用与“DISALLOW_COPY_AND_ASSIGN”运算符的相反的拷贝构造函数。作为一个C++新手,除非我错过了一些东西,否则我会发现这两个建议互相冲突。

所以我的问题是:是否没有银弹,允许有效的引用返回对象内部不容易晃动指针? const引用返回的效果如何?另外,我应该而不是经常使用私人对象字段的指针吗?根据价值或指针选择何时存储对象的私有实例字段的一般规则是什么?

(编辑)为了清楚起见,迈尔斯例如悬空指针代码:

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的Points被破坏。这反过来,将离开pUpperLeft指向不再存在的对象“迈尔斯,有效的C++(第28项)

我觉得他是在暗示将要返回的值点来避免这一点:

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

哦,我不得不同意迈尔斯那么。仅仅因为客户端可能会误用这些函数并由于销毁Rectangle而导致引用无效,而不是按值返回所有内容的理由,所以通过const引用而不是按值返回Point。客户应该足够了解他需要一个被返回的点的副本(在这种情况下,当你通过const引用返回时,他仍然可以这样做)。我最喜欢迈尔斯,但这本书像12岁。 Sutter在这种情况下会与他矛盾,并建议Point应该通过const参考返回 – stinky472 2010-06-27 12:57:52

+1

以避免'pessimization'[...]。尽管如此,如果这就像一个窗口句柄被返回到控件,你通常希望避免返回它,因为它会放弃你的类保持不变量的能力。 – stinky472 2010-06-27 12:58:37

回答

5

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

在正常情况下,我建议遵循Effective C++中的指导原则通常被认为是一件好事;在你的具体情况下,返回一个对象而不是任何对内部对象的引用通常是正确的。大多数编译器在处理大的返回值方面非常出色(Google for Return Value Optimization,几乎每个编译器都这么做)。

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

+0

Bah!根据维基百科的说法,它似乎更有效C++详细讨论了这个问题(我还没有这方面的内容)。在暴露出可用于生产的代码之前,如何学习C++的怪癖是非常残酷的。不过,谢谢:) – Glitz 2010-06-27 12:56:15

+0

值得注意的是,使用RVO的编译器通常会使用它来优化不必要的临时对象拷贝。他们不一定会做出如此出色的工作来优化复制,以实现简单的访问器或按值返回UDT。 – stinky472 2010-06-27 13:00:24

0

这真的取决于情况如果你打算看到你想通过引用传递的调用方法的变化请记住传值是一个相当繁重的操作它需要调用复制构造函数本质上必须分配和存储足够的内存以适合您的物体尺寸。

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

尽量限制通过价值,如果你可以,除非你必须。

2

首先,让我们来看看这条语句上下文:

根据有效的C++(第28项), “避免返回的句柄(引用, 指针,或迭代器)为对象 内部它增加封装, 可以帮助const成员函数执行 const,并最大限度地减少创建 悬挂手柄。“

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

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

您希望通过在此Button类中提供按钮作为方法所能做的所有事情来避免这种Button类中的这些情况。然后,您不需要返回句柄到特定于操作系统的按钮句柄。

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

delete my_scoped_ptr.get(); // wrong 

不过,需要这种妥协,因为有在那里我们有需要定期指针在传递C/C++ API的工作很多情况下,妥协往往是必需的以满足不接受你的特定班级但接受其内部组件的图书馆。

在你的情况下,试着想一下,如果你的类可以通过这种方式避免返回内部函数,而不是通过你的公共接口提供函数来完成内部任务。如果没有,那么你已经做了你所能做的一切;你必须返回一个指针或引用,但将它记录为特殊情况是一个好习惯。如果您知道哪些地方需要提前访问课程内部,您还应该考虑使用朋友;通过这种方式,您可以保持这些访问方法的私密性,并且不会对其他人无法访问。

返回由值对象是唯一 办法,我能想到的,以避免返回 手柄。这对我来说,我建议我应该尽可能地使用 的值返回私有对象内部。

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

+0

谢谢!特别是明智地使用朋友。我明白,const引用不允许私人内部被篡改,但是这与我有关:“句柄是一个指针,一个引用还是一个迭代器并不重要,它是否与const限定无关。返回句柄的成员函数本身是否是const并不重要,重要的是返回一个句柄,因为一旦这个句柄完成,你就会冒着句柄超过它引用的对象的风险。这种危险是不可避免的吗?我应该在这种情况下使用shared_ptr吗? – Glitz 2010-06-27 11:26:31

+0

@Glitz它取决于手柄的含义。如果句柄是一个指针或允许可变访问的东西,那么以任何方式返回它都将允许客户端篡改内部。不过,我很确定迈耶斯在这个特殊的可变情况下使用了“处理”这个术语。例如,返回int * const不会阻止用户篡改整数指针。你能否提供一些关于你的特殊情况的更多信息?一些示例代码也许? – stinky472 2010-06-27 11:56:36

+0

如果在这个意义上它是*句柄*,那么按值返回它同样会有问题,因为句柄的副本仍然基本指向相同的东西。避免这种情况的唯一方法是避免以任何形式或形式返回句柄,并且让你的班级完全负责任何事情。如果你无法避免这种情况,那么你必须作出妥协(但仍然认为是朋友,如果这样做不起作用,至少要记录这个函数,以便客户不会试图仅仅使用句柄而忽略你的类) 。 – stinky472 2010-06-27 11:59:42