繁体   English   中英

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

[英]C++ using scoped_ptr as a member variable

只是想要关于设计问题的意见。 如果您拥有一个C ++类而不是拥有其他对象,那么您会使用智能指针来实现这一点吗?

class Example {
public: 
  // ...

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

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

我的观点是,一方面,您可以清楚地知道该对象已拥有并确保将其删除,但另一方面,您可以轻松地拥有一个常规指针并将其删除到析构函数中。 这是过度杀伤力吗?

后续行动:只想对您的所有回答表示感谢。 感谢您对有关auto_ptr的注意,在复制整个对象时将另一个对象留有NULL指针,我已经广泛使用了auto_ptr,但还没有想到。 除非我有充分的理由,否则我基本上将所有类都设为boost :: noncopyable,因此不必担心。 同时也感谢您提供有关异常中内存泄漏的信息,这也很高兴。 我尽量不要写任何可能在构造函数中导致异常的东西-这样做有更好的方法-这样就不成问题。

我只是有另一个问题。 当我问这个问题时,我想知道是否有人真正做到了这一点,而且大家似乎都提到从理论上讲这是一个好主意,但是没有人说他们实际上做了。 这让我感到惊讶! 当然,拥有指向另一个物体的指针的对象并不是一个新主意,我希望大家在某个时候都可以做到这一点。 这是怎么回事?

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

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

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

  • 所有权转让:智能指针是可移动的
  • 所有权共享:智能指针是可复制的。 如果智能指针已经是可复制的,则很容易支持所有权转移语义:这仅是原子复制和原始复位操作,将其限制为某些类型的智能指针(例如,仅临时智能指针)。

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

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

auto_ptr有一个大问题,因为它使用复制构造函数实现了Movable概念。 这是因为当auto_ptr被C ++接受时,与新的C ++标准相对,还没有一种方法可以使用move构造函数来本地支持move语义。 也就是说,您可以使用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,则将从.cpp文件进行对scoped_ptr的析构函数的隐式调用,您可以在其中放置Owned类的定义。

您对auto_ptr遇到同样的问题,但还有另一个问题:为auto_ptr提供不完整的类型当前是未定义的行为(也许将在下一个C ++版本中得到修复)。 因此,当您使用auto_ptr时, 必须在标头文件中将“拥有的个人”设置为完整类型。

shared_ptr没问题,因为它使用了多态删除器,该删除器对删除进行了间接调用。 因此,删除函数不会在实例化析构函数时实例化,而是在shared_ptr的构造函数中创建删除程序时才会实例化。

这是一个好主意。 它有助于简化代码,并确保当您在对象的生存期内更改“拥有”对象时,前一个对象被正确销毁了。

您必须记住,scoped_ptr是不可复制的,这使您的类在默认情况下不可复制,直到/除非您添加自己的副本构造函数,等等。(当然,在使用原始指针的情况下,使用默认副本构造函数将是非复制操作,也不行!)

如果您的类具有多个指针字段,那么在以下情况下,使用scoped_ptr实际上可以提高异常安全性:

class C
{
  Owned * o1;
  Owned * o2;

public:
  C() : o1(new Owned), o2(new Owned) {}
  ~C() { delete o1; delete o2;}
};

现在,想象一下在构造C的过程中,第二个“新拥有的”会抛出异常(例如,内存不足)。 o1将被泄漏,因为不会调用C ::〜C()(析构函数),因为该对象尚未完全构造。 确实会调用任何完全构造的成员字段的析构函数。 因此,使用scoped_ptr而不是普通指针将允许o1被正确销毁。

一点也不算过分,这是个好主意。

但是,这确实需要您的班级客户了解升压。 这可能是问题,也可能不是问题。 对于可移植性,您可以考虑使用std :: auto_ptr来完成(在这种情况下)相同的工作。 由于它是私人的,因此您不必担心其他人试图复制它。

使用scoped_ptr是一个好主意。

保持和手动销毁指针并不像您想的那么简单。 特别是如果您的代码中有多个RAW指针。 如果异常安全性和不泄漏内存是优先事项,那么您需要大量额外的代码来使其正确。

首先,必须确保正确定义所有四个默认方法。 这是因为这些方法的编译器生成版本适用于普通对象(包括智能指针),但在正常情况下会导致指针处理问题(查找“浅复制问题”)。

  • 默认构造函数
  • 复制构造函数
  • 赋值运算符
  • 析构函数

如果使用scoped_ptr,则无需担心其中任何一个。

现在,如果您的类中有多个RAW指针(或构造函数的其他部分可以抛出)。 您必须在构造和销毁过程中明确处理异常。

class MyClass
{
    public:
        MyClass();
        MyClass(MyClass const& copy);
        MyClass& operator=(MyClass const& copy);
        ~MyClass();

    private
        Data*    d1;
        Data*    d2;
};

MyClass::MyClass()
    :d1(NULL),d2(NULL)
{
    // This is the most trivial case I can think off
    // But even it looks ugly. Remember the destructor is NOT called
    // unless the constructor completes (without exceptions) but if an
    // exception is thrown then all fully constructed object will be
    // destroyed via there destructor. But pointers don't have destructors.
    try
    {
        d1 = new Data;
        d2 = new Data;
    }
    catch(...)
    {
        delete d1;
        delete d2;
        throw;
    }
}

看看scopted_ptr有多容易。

有作用域的指针正是因为它可以确保删除对象而无需担心程序员的情况,因此可以很好地做到这一点。 我认为这是作用域ptr的很好使用。

我发现,一个好的设计策略通常是避免尽可能多地手动释放内存,并让您的工具(在这种情况下为智能指针)为您完成任务。 正如我所看到的,手动删除是不好的,主要原因之一是,代码变得很难快速维护。 内存的分配和释放逻辑通常在代码中是分开的,这导致互补行无法保持在一起。

我认为这并不过分,它比具有原始指针更好地记录了成员的语义,并且不容易出错。

为什么要大刀阔斧? boost :: scoped_ptr非常易于优化,我敢打赌,生成的机器代码将与您在析构函数中手动删除指针相同。

scoped_ptr很好-只需使用它即可:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM