怎么可以实现std :: auto_ptr的复制构造函数?

时间:2010-12-22 22:00:10

标签: c++ smart-pointers copy-constructor auto-ptr

回到我疯狂的AutoArray thingy ...(引用那里的重要部分:

class AutoArray
{
    void * buffer;
public:
    //Creates a new empty AutoArray
    AutoArray();
    //std::auto_ptr copy semantics
    AutoArray(AutoArray&); //Note it can't be const because the "other" reference
                           //is null'd on copy...
    AutoArray& operator=(AutoArray);
    ~AutoArray();
    //Nothrow swap
    // Note: At the moment this method is not thread safe.
    void Swap(AutoArray&);
};

无论如何,试图实现复制构造函数。有一段客户端代码(尚未提交到bitbucket,因为它不会构建)看起来像这样:

AutoArray NtQuerySystemInformation(...) { ... };

AutoArray systemInfoBuffer = NtQuerySystemInformation(...);

这会失败,因为复制构造函数将非const引用作为参数....但我不知道如何修改复制构造函数以获取const引用,给定指派中使用的来源AutoArray已被修改(因此不会是const)。当然,你无法修改要使用的值,因为它是复制构造函数,并且它是一个无限循环!

如果我使用auto_ptr,这将是有效的:

std::auto_ptr NtQuerySystemInformation(...) { ... };

std::auto_ptr systemInfoBuffer = NtQuerySystemInformation(...);

那么,如果一个具有auto_ptr复制语义的类可以吗?

3 个答案:

答案 0 :(得分:14)

auto_ptr使用了一个肮脏的技巧。

我将使用名为auto_int的简化类来演示复制构造功能,而不会带来模板或继承引入的任何复杂性。我认为代码大多是正确的,但它没有经过测试。我们的基本auto_int看起来像这样:

class auto_int
{
public:

    auto_int(int* p = 0) : p_(p) { }

    ~auto_int() { delete p_; }

    // copy constructor taking a non-const reference:
    auto_int(auto_int& other) 
        : p_(other.release()) { }

    int* release() 
    {
        int* temp = p_;
        p_ = 0;
        return temp;
    }

private:

    int* p_;
};

使用此基本auto_int,我们无法复制临时对象。我们的目标是能够写出如下内容:

auto_int p(auto_int(new int()));

我们可以做的是使用助手类。对于auto_ptr,这称为auto_ptr_ref。我们会致电我们auto_int_ref

class auto_int;

class auto_int_ref 
{
public:
    auto_int_ref(auto_int* p) : p_(p) { }

    auto_int& ref() { return *p_; }

private:
    auto_int* p_;
};

基本上,这个类的一个实例只存储一个指向auto_int的指针,并允许我们将它用作"引用"到auto_int

然后在我们的auto_int课程中,我们需要两个额外的功能。我们需要另一个带auto_int_ref的构造函数,我们需要一个转换运算符,允许auto_int隐式转换为auto_int_ref

auto_int(auto_int_ref other)
    : p_(other.ref().release()) { }

operator auto_int_ref() { return this; }

这将使我们能够复制"一个临时的,但仍然有复制构造函数采取非const引用。如果我们再看一下示例代码:

auto_int p(auto_int(new int()));

我们会构建一个新的临时auto_int并将new int()传递给采用int*的构造函数。然后,使用auto_int_ref将此临时转换为指向它的operator auto_int_ref(),使用auto_int的{​​{1}}构造函数初始化auto_int_ref

答案 1 :(得分:3)

auto_ptr的副本ctor通过从传入的对象中取消所有权来工作。这也是auto_ptr无法在vector或其他STL集合中使用的重要原因。

这不是大多数复制构造函数的工作方式。通常,您的复制构造函数将只克隆传入的对象,因此您可以将const引用传递给它。但是auto_ptr并没有这样做。它实际上修改了原始对象。从这个意义上说,它只是一个名称的复制构造函数,而不是语义。

EDIT2:

我正试着把它煮沸一点。因此,你有效地尝试对你的类做一些允许语法类似的事情:

#include <string>
#include <memory>
using namespace std;

auto_ptr<string> gimme()
{
    return auto_ptr<string>(new string("Hello"));
}

int main()
{
    auto_ptr<string> s = gimme();
}

...而且您想知道如何让s = gimme()部分发挥作用。正确?

这里的秘密在于代理类,auto_ptr_ref由标准在20.4.5中描述,其目的是“允许auto_ptr对象传递给函数并从函数返回。”:

namespace std {
  template <class Y> struct auto_ptr_ref {};
  template<class X> class auto_ptr {
  public:
    typedef X element_type;

    // 20.4.5.1 construct/copy/destroy:
    explicit auto_ptr(X* p =0) throw();
    auto_ptr(auto_ptr&) throw();
    template<class Y> auto_ptr(auto_ptr<Y>&) throw();
    auto_ptr& operator=(auto_ptr&) throw();
    template<class Y> auto_ptr& operator=(auto_ptr<Y>&) throw();
    auto_ptr& operator=(auto_ptr_ref<X> r) throw();
    ˜auto_ptr() throw();
    // 20.4.5.2 members:
    X& operator*() const throw();
    X* operator->() const throw();
    X* get() const throw();
    X* release() throw();
    void reset(X* p =0) throw();
    // 20.4.5.3 conversions:
    auto_ptr(auto_ptr_ref<X>) throw();
    template<class Y> operator auto_ptr_ref<Y>() throw();
    template<class Y> operator auto_ptr<Y>() throw();
  };
}

答案 2 :(得分:0)

没有从T*std::auto_ptr<T>的隐式转换。我猜你确实有一个从NTSTATUS句柄到AutoArray的隐式转换构造函数。但是如果该转换创建了一个临时的,那么临时就无法复制。

如果您使用直接初始化而不是复制初始化,则您的“问题”可能会消失。

AutoArray systemInfoBuffer( ntDll.NtQuerySystemInformation(
  Dll::NtDll::SystemProcessInformation) );