How can I implement the copy std :: auto_ptr constructor?

Back to my crazy AutoArray thingy ... (quoting important bits from there:

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

)

In any case, an attempt to implement a copy constructor. There is a fragment of client code (not yet included in the bitpack because it will not be created), which looks like this:

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

This fails because the copy constructor accepts a non const reference as an argument .... but I donโ€™t see how you could change the copy constructor to take a const reference, given that the AutoArray source used in the assignment changes (and therefore there will be no const ). You cannot change things to use a pass by value, of course, because it is a copy constructor, and it will be an infinite loop!

If I used auto_ptr , this would be true:

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

How then is a class with auto_ptr semantics possible?

+6
c ++ copy-constructor smart-pointers auto-ptr
source share
3 answers

auto_ptr uses a dirty trick.

I use a class called auto_int to demonstrate only the functionality of building a copy without any complexity introduced by templates or inheritance. I think the code is mostly correct, but it has not been verified. Our main auto_int looks 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 cannot copy a temporary object. Our goal is 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 will call our 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 simply stores a pointer to auto_int and allows us to use it as a reference to auto_int .

Then in our auto_int class we need two additional functions. We need another constructor that accepts auto_int_ref , and we need a conversion operator that allows auto_int implicitly convert to 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โ€ the temporary object while still having the copy constructor using a non-constant reference. If we look again at our sample code:

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

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

+14
source share

auto_ptr copy ctor works, removing ownership from the transferred object. This is also a big part of the reason auto_ptr cannot be used in vector or other STL collections.

This is not how most copy constructors work. Usually, your copy constructor simply clones the passed object, so you can pass it a reference to const. But auto_ptr does not do this. In fact, he changed the original object. In this sense, its only copy constructor is by name, not semantics.

EDIT2:

I'm trying to cook it a little. So effective that you are trying to do something with your class, which will allow syntax like 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 are wondering how to make the s = gimme() work. Correctly?

The secret here is in the proxy class auto_ptr_ref , described by the standard in 20.4.5, the purpose of which is to "allow auto_ptr objects to transfer and return 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(); }; } 
+3
source share

There is no implicit conversion from T* to std::auto_ptr<T> . I assume that you have an implicit conversion constructor from the NTSTATUS descriptor to AutoArray . But if this conversion creates a temporary one, this temporary copy cannot be copied.

If you use direct initialization instead of initializing the copy, your โ€œproblemโ€ may disappear.

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

All Articles