0

まず、std::auto_ptr に 3 つのバージョンがあることを確認してください。

次のような std::auto_ptr の最初のバージョン:

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

実装は簡単ですが、インターフェイスのみです。

私のコードは次のようになります:

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

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

私の見解では、私のテスト コードはコンパイラを通過できません。しかし、それは合格し、実行すると、ポンターの削除で2回壊れました。

アセンブリ コードをトレースすると、次のようなフローが見つかりました。メモリ アドレスは略して下位 16 ビットです。

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

コードのようです: p = foo(); 一時オブジェクトを ctor し、foo() で新しいメモリを保持します。

重要な点は、なぜ p = foo() が p.operator=() を呼び出す以外に p.pointee を変更するのかということです。

1 つ目の auto_ptr の実装を追加します。


ネットの友達と話して、彼はmybe コンパイラが生成することを指摘しました:

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

使用以外

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

std::auto_ptr には 2 つの operator= があることがわかりました。そして、コンパイラのヒント:「「auto_ptr」は「auto_ptr&」に変換できません」ながら、手動でインターフェイスに追加してテストします。

それが鍵です!!! 次に、理由を見つける必要があります。


ユーザーがクラス型の operator= を定義していない場合、コンパイラはそれを生成します。そして、他の operator= と比較して、より特別なものを選択してください!

解決しました!あなたのすべての答えを考えます!コメントありがとうございます!

4

1 に答える 1

1

あなたの問題を正しく理解しているかどうかはわかりませが、「template copy op =" は真のコピー op= ではないため (および" template copy ctor" は真の copy ctor ではありません)。

問題を示す簡単な例を次に示します。

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

それは印刷します:

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

2 番目の割り当てに 1 行しかないのはなぜですか? これは、真のコピー代入演算子をc = d;呼び出すためであり、宣言していないため (テンプレートバージョンのみ)、コンパイラが自動的に生成します (メンバーごとの代入を行うため、 への呼び出し)。Foo<int>::operator=(Foo<int> const&)M::operator=

したがって、クラスに明示的に追加する必要があります。

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

そして、それは印刷します:

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

したがって、あなたの例でp = foo();は、ユーザー定義を呼び出すのではなく、(ソースをリリースせずに) メンバーtemplate<class U> auto_ptr<T>& operator=(auto_ptr<U>& rhs)を割り当てるだけの暗黙的に生成されたバージョンを呼び出します。pointee

于 2013-09-07T17:32:02.377 に答える