C++ 使用 scoped_ptr 作为成员变量

标签 c++ oop smart-pointers

只是想就一个设计问题发表意见。如果你有一个 C++ 类而不拥有其他对象,你会使用智能指针来实现这一点吗?

class Example {
public: 
  // ...

private:
  boost::scoped_ptr<Owned> data;
};

“拥有”对象不能按值存储,因为它可能会在对象的生命周期内发生变化。

我的观点是,一方面,您明确该对象是拥有的并确保将其删除,但另一方面,您可以轻松地拥有一个常规指针并在析构函数中将其删除。这是不是矫枉过正?

跟进:只是想对您的所有回答表示感谢。感谢您对 auto_ptr 在复制整个对象时为另一个对象留下一个 NULL 指针的提醒,我已经广泛使用了 auto_ptr 但还没有想到这一点。除非我有充分的理由,否则我基本上将所有类(class)都设为 boost::noncopyable,因此无需担心。还要感谢有关异常中内存泄漏的信息,这也很高兴知道。我尽量不在构造函数中编写可能导致异常的东西 - 有更好的方法来做到这一点 - 所以这不应该是一个问题。

不过,我还有另一个问题。当我问这个问题时,我想知道是否有人真的这样做了,你们似乎都提到理论上这是一个好主意,但没有人说他们真的这样做了。这让我感到惊讶!当然,一个对象拥有指向另一个对象的指针并不是一个新想法,我原以为你们都曾在某个时候做过。怎么回事?

最佳答案

scoped_ptr 非常适合此目的。但是必须了解它的语义。您可以使用两个主要属性对智能指针进行分组:

  • 可复制:可以复制智能指针:拷贝和原始共享所有权。
  • 可移动:可以移动智能指针:移动结果将拥有所有权,原始的将不再拥有。

这是相当常见的术语。对于智能指针,有一个特定的术语可以更好地标记这些属性:

  • 所有权转移:智能指针是可移动的
  • 所有权共享:智能指针是可复制的。如果智能指针已经是可复制的,那么很容易支持所有权转移语义:那只是一个原子 copy & reset-of-original 操作,将其限制为某些类型的智能指针(例如,只有临时智能指针)。

让我们使用 (C)opyable(M)ovable, (N)either 对可用的智能指针进行分组:

  1. boost::scoped_ptr: N
  2. std::auto_ptr: M
  3. boost::shared_ptr: C

auto_ptr 有一个大问题,它使用复制构造函数来实现 Movable 概念。这是因为当 auto_ptr 被 C++ 接受时,与新的 C++ 标准相反,还没有一种方法可以使用移动构造函数本地支持移动语义。也就是说,您可以使用 auto_ptr 执行以下操作,并且它可以工作:

auto_ptr<int> a(new int), b;
// oops, after this, a is reset. But a copy was desired!
// it does the copy&reset-of-original, but it's not restricted to only temporary
// auto_ptrs (so, not to ones that are returned from functions, for example).
b = a; 

无论如何,正如我们所见,在您的情况下,您将无法将所有权转移给另一个对象:您的对象实际上是不可复制的。在下一个 C++ 标准中,如果您使用 scoped_ptr,它将是不可移动的。

要使用 scoped_ptr 实现您的类,请注意您是否满足以下两点之一:

  • 在你的类的 .cpp 文件中写一个析构函数(即使它是空的),或者
  • 使 Owned 成为一个完全定义的类。

否则,当你创建一个 Example 的对象时,编译器会为你隐式定义一个析构函数,它会调用 scoped_ptr 的析构函数:

~Example() { ptr.~scoped_ptr<Owned>(); }

这将使 scoped_ptr 调用 boost::checked_delete,如果您没有完成上述两点,则会提示 Owned 不完整。如果您已经在 .cpp 文件中定义了自己的 dtor,那么对 scoped_ptr 析构函数的隐式调用将从 .cpp 文件中进行,您可以在其中放置 Owned 类的定义。

您对 auto_ptr 也有同样的问题,但还有一个问题:为 auto_ptr 提供不完整的类型目前是未定义的行为(可能会在下一个 C++ 版本中修复)。因此,当您使用 auto_ptr 时,您必须在头文件中使 Owned 成为一个完整的类型。

shared_ptr 没有这个问题,因为它使用了一个多态删除器,它会间接调用删除。所以deleting函数不是在析构函数被实例化的时候被实例化,而是在shared_ptr的构造函数中创建deleter的时候。

关于C++ 使用 scoped_ptr 作为成员变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/500656/

相关文章:

c++ - 为什么在赋值运算符重载中不需要 get 函数?

c++ - 在 CodeBlocks 中编译 C++ 代码时出现多重定义错误

oop - 将依赖项注入(inject)外观类中包含的类

指向静态和动态分配资源的 C++ 智能指针

c++ - 使用shared_ptr时无法解析的外部符号

c++ - 具有初始化列表的 QVector<int> 因 3 项而失败

c++ - C++中的X11-调整窗口大小时只要设置ResizeRedirectMask,就会发生裁剪

c++ 使用 std::list 隐式复制 *this

javascript - 调用javascript对象自身的属性

c++ - 无法访问同一类的私有(private)成员