How could one implement std::auto_ptr's copy constructor?

这一生的挚爱 提交于 2019-11-27 14:51:04

auto_ptr uses a dirty trick.

I'll use a dumbed-down class named auto_int to demonstrate just the copy construction functionality without bringing in any complexities introduced by templates or inheritance. I think the code is mostly correct, but it's untested. Our basic auto_int look something like this:

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

With this basic auto_int, we can't copy a temporary object. Our goal is to be able to write something like:

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

What we can do is use a helper class. For auto_ptr, this is called auto_ptr_ref. We'll call ours 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_;
};

Basically, an instance of this class just stores a pointer to an auto_int and allows us to use it as a "reference" to an auto_int.

Then in our auto_int class we need two additional functions. We need another constructor that takes an auto_int_ref and we need a conversion operator that allows an auto_int to be implicitly converted to an auto_int_ref:

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

operator auto_int_ref() { return this; }

This will allow us to "copy" a temporary while still having the copy constructor take a non-const reference. If we look again at our sample code:

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

What happens is we construct a new temporary auto_int and pass new int() to the constructor that takes an int*. This temporary is then converted to an auto_int_ref that points to it, using the operator auto_int_ref(), and the auto_int constructor that takes an auto_int_ref is used to initialize p.

auto_ptr's copy ctor works by taking ownership away from the passed-in object. This is also a big part of the reason why auto_ptr can't be used in a vector or other STL collections.

This is not how most copy constructors work. Usually your copy constructor will just clone the passed-in object, so you can pass a const reference to it. But auto_ptr doesnt do this. It actually modified the original object. In this sense, its only a copy constructor by name, not by semantics.

EDIT2:

I'm trying to boil this down a bit. So effectively you're trying to do something with your class that will allow syntax similar to this:

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

...and you're wondering how to get the s = gimme() part to work. Right?

The secret here is in a proxy class, auto_ptr_ref described by the Standard in 20.4.5 whose purpose is "to allow auto_ptr objects to be passed to and returned from functions.":

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

There is no implicit conversion from T* to std::auto_ptr<T>. I'm guessing you do have an implicit conversion constructor from NTSTATUS handle to AutoArray. But if that conversion creates a temporary, that temporary can't be copied.

If you use direct initialization rather than copy initialization, your "problem" may go away.

AutoArray systemInfoBuffer( ntDll.NtQuerySystemInformation(
  Dll::NtDll::SystemProcessInformation) );
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!