Domanda

First please make sure you know the std::auto_ptr once have 3 versions.

The 1st version of std::auto_ptr which look like this:

template<class T>
class auto_ptr 
{
public:
    explicit auto_ptr(T *p = 0): pointee(p) {}

    // copy constructor member
    // template: initialize a new auto_ptr with any compatible auto_ptr
    template<class U> auto_ptr(auto_ptr<U>& rhs): pointee(rhs.release()) {}

    ~auto_ptr() { delete pointee; }

    // assignment operator
    // member template assign from any compatible auto_ptr 
    template<class U> auto_ptr<T>& operator=(auto_ptr<U>& rhs)
    {
        if (&rhs != this)
        {
            reset(rhs.release());
        }
        return *this;
    }

    T& operator*() const { return *get(); }
    T* operator->() const { return get(); } 

    // return value of current dumb pointer
    T* get() const { return pointee; } 

    // relinquish ownership of current dumb pointer and return its value
    T* release() { T* p = pointee;  pointee = 0; return p; }                            

    // delete owned pointer,assume ownership of p
    void reset(T *p = 0)
    { 
        if (p != pointee) 
        {
            delete pointee;
            pointee = p;
        }
    }
private:
    T* pointee;
};      

Only interface, while the implementation is easy.

My code like this:

auto_ptr<int> foo()
{
    auto_ptr<int> p(new int(1));
    return p;
}

int main()
{
    auto_ptr<int> p;
    p = foo();
    return 0;
}

In my point of view, my test code can not pass compiler. But it passed, and when I run it, it broken for delete a ponter twice.

I trace the assembly code and find the flow like below, memory address is low 16 bit for short.

ctor: f8e4    
new: 6bf0
ctor: f7d4

copy ctor: f7d4  ->  f80c
dctor: f7d4 (NULL)
delete: 0

lea ecx, [ebp-0ECh] // f8e4: memory-> 6bf0

dctor: f80c (6bf0)
delete: 6bf0

dctor: f8e4 (6bf0)   // twice delete

It seems that the code : p = foo(); ctor a temporary object and it hold the memory which new in foo().

The key point is, why p = foo() just change p.pointee, other than call p.operator=() ?

I add implementation of 1st auto_ptr.


Talking with net friend, he pointed that mybe compiler generate:

auto_ptr<T>& operator=(auto_ptr<T>& rhs)

other than use

template<class U> auto_ptr<T>& operator=(auto_ptr<U>& rhs);

I find that std::auto_ptr have two operator=. And I test add it to interface manually, while complier tips: "'auto_ptr' can not convert to 'auto_ptr &'".

that's the Key!!! then I need to find Why!


When user not define operator= for the class type, compiler will generate one. And compare with other operator=,choose a more special one!

Solved! Thinks for all your answer! Thanks for all your comments!

È stato utile?

Soluzione

I'm not sure that I have understood your issue correctly, but your interface actually does not define the copy assignment operator (nor the copy constructor), because the "template copy op=" is not a true copy op= (and the "template copy ctor" is not a true copy ctor).

Here's a simple example to show the problem:

#include <cstdio>
using std::puts;

struct M {
    M& operator=(M const&) {
        puts("M::operator=(M const&)");
        return *this;
    }
};

template<typename T> class Foo {
    M m;
    template<typename U> friend class Foo; // (needed for m = rhs.m; below)
public:
    template<typename U> Foo& operator=(Foo<U> const& rhs) {
        puts("[template] Foo<T>::operator=(Foo<U> const&)");
        m = rhs.m; // calls M's op=
        return *this;
    }
};

int main() {
    puts("===");
    Foo<int> a;
    Foo<double> b;
    a = b;
    puts("---");
    Foo<int> c;
    Foo<int> d;
    c = d;
    puts("===");
}

That prints:

===
[template] Foo<T>::operator=(Foo<U> const&)
M::operator=(M const&)
---
M::operator=(M const&)
===

Why is there only one line for the second assignment? That's because c = d; calls Foo<int>::operator=(Foo<int> const&) i.e. the true copy assignment operator, and since we have not declared it (only a template version) the compiler generates it automatically (which does a member-wise assignment, hence the call to M::operator=).

Thus, me must add it explicitly to the class:

    // ...
    Foo& operator=(Foo const& other) {
        puts("[non-template] Foo<T>::operator=(Foo<T> const&)");
        m = other.m;
        return *this;
    }
};

and then it prints:

===
[template] Foo<T>::operator=(Foo<U> const&)
M::operator=(M const&)
---
[non-template] Foo<T>::operator=(Foo<T> const&)
M::operator=(M const&)
===

So, in your example, p = foo(); does not invoke your user-defined template<class U> auto_ptr<T>& operator=(auto_ptr<U>& rhs) but the implicitly-generated version that simply assigns the pointee member (without releasing the source).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top