当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《C++使用scoped_ptr作为成员变量》经验,为你挑选了4个好方法。

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

class Example {
public: 
  // ...

private:
  boost::scoped_ptr data;
};

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

我的观点是,一方面,你明确表示对象是拥有的并确保删除它,但另一方面,你可以很容易地只有一个常规指针并在析构函数中删除它.这有点矫枉过正吗?

跟进:只是想感谢您的所有答案.感谢关于auto_ptr的抬头在复制整个对象时使用NULL指针留下另一个对象,我已经广泛使用了auto_ptr但是还没想到.除非我有充分的理由,否则基本上我的所有类都是boost :: noncopyable,所以没有什么可担心的.还要感谢有关异常中内存泄漏的信息,这也是很好的.我尽量不编写可能导致构造函数中的异常的东西 - 有更好的方法可以做到这一点 - 所以这应该不是问题.

我只是有另一个问题.当我问这个问题时,我想知道是否有人真的这样做了,你们似乎都提到理论上这是一个好主意,但没有人说他们真的这样做了.这让我感到惊讶!当然,一个拥有指向另一个对象的对象并不是一个新想法,我希望你们在某个时刻之前都会做到这一点.这是怎么回事?



1> Johannes Sch..:

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

可复制:可以复制智能指针:副本和原始共享所有权.

移动:智能指针可以移动:这一举措,结果将拥有所有权,原来不会再拥有.

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

所有权转移:智能指针是Movable

所有权份额:智能指针是可复制的.如果一个智能指针已经是可复制,可以很容易地支持转移的所有权语义:那则只是一个原子的副本及复位的原装操作,限制,为某些类型(如只是暂时的智能指针)的智能指针.

让我们集团现有的智能指针,使用(C)opyable,以及(M)ovable,(N)either:

    boost::scoped_ptr:N

    std::auto_ptr:M

    boost::shared_ptr: C

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

auto_ptr 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(); }

那将会使scoped_ptr调用boost::checked_delete,Owned如果你没有完成上述两点中的任何一点,就会抱怨它是不完整的.如果您在.cpp文件中定义了自己的dtor,则对.scpp文件的析构函数的隐式调用将来自.cpp文件,您可以在其中放置Owned类的定义.

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

shared_ptr没有这个问题,因为它使用多态删除器,它间接调用删除.因此,在析构函数实例化时不会实例化删除函数,但是在shared_ptr的构造函数中创建删除函数.



2> SCFrench..:

这是个好主意.它有助于简化代码,并确保在对象的生命周期内更改Owned对象时,前一个对象会被正确销毁.

你必须记住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.



3> CB Bailey..:

它根本不是矫枉过正,这是个好主意.

但它确实要求您的班级客户了解提升.这可能是也可能不是问题.为了便于携带,您可以考虑使用std :: auto_ptr(在这种情况下)执行相同的工作.由于它是私有的,您不必担心其他人试图复制它.


通过原始指针保存资源意味着您**提供(或禁止)自定义复制和分配操作.我读了这个.更改为std :: auto_ptr不会更改此设置,但更改为scoped_ptr可以免费进行抑制.

4> Martin York..:

使用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有多容易。

推荐阅读
mobiledu2402851173
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有