6

回到我疯狂的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' 的复制语义的类如何成为可能呢?

4

3 回答 3

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_;
};

有了这个 basic 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_;
};

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

然后在我们的auto_int类中,我们需要两个额外的函数。我们需要另一个接受 an 的构造函数,auto_int_ref并且我们需要一个允许将 anauto_int隐式转换为 an的转换运算符auto_int_ref

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

operator auto_int_ref() { return this; }

这将允许我们“复制”一个临时的,同时仍然让复制构造函数采用非常量引用。如果我们再看一下我们的示例代码:

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

发生的情况是我们构造了一个新的临时对象auto_int并传递new int()给接受int*. 然后使用 将这个临时变量转换为auto_int_ref指向它的 an operator auto_int_ref(),并使用带有 an 的auto_int构造函数auto_int_ref来初始化p

于 2010-12-22T22:14:03.353 回答
3

auto_ptr的复制 ctor 的工作原理是从传入的对象中获取所有权。这也是为什么auto_ptr不能在一个vector或其他 STL 集合中使用的很大一部分原因。

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

编辑2:

我试图把它简化一下。如此有效地,您正在尝试对您的类做一些允许类似于此的语法的事情:

#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();
  };
}
于 2010-12-22T22:05:15.630 回答
0

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

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

AutoArray systemInfoBuffer( ntDll.NtQuerySystemInformation(
  Dll::NtDll::SystemProcessInformation) );
于 2010-12-22T22:13:33.733 回答