3

自動ポインターの実装があります。

template <typename T, bool Arr = false>
class GAutoPtr
{
    T *Ptr;

public:
    typedef GAutoPtr<T, Arr> &AutoPtrRef;

    GAutoPtr(T *ptr = 0)
    {
        Ptr = ptr;
    }

    GAutoPtr(AutoPtrRef p)
    {
        Ptr = p.Release();
    }

    ~GAutoPtr() { Empty(); }
    operator T*() { return Ptr; }
    T *Get() { return Ptr; }
    T *operator->() const { LgiAssert(Ptr); return Ptr; }

    inline void Empty()
    {
        if (Arr)
            delete [] Ptr;
        else
            delete Ptr;
        Ptr = 0;
    }

    AutoPtrRef operator =(GAutoPtr<T> p)
    {
        Empty();
        Ptr = p.Ptr;
        p.Ptr = 0;
        return *this;
    }

    void Reset(T *p)
    {
        if (p != Ptr)
        {
            Empty();
            Ptr = p;
        }
    }

    T *Release()
    {
        T *p = Ptr;
        Ptr = 0;
        return p;
    }
};

typedef GAutoPtr<char, true> GAutoString;
typedef GAutoPtr<char16, true> GAutoWString;

Visual C++ 6 では問題なく動作します。ただし、Visual C++ 2005 または 2008 では、関数から自動ポインタを返すことはできません。

例えば

GAutoString Func()
{
    char *s = new char[4];
    strcpy(s, "asd");
    return s;
}

int main()
{
    GAutoString a = Func();
    /// a.Ptr is now garbage
}

何が起こるかというと、コンパイラは一時的な GAutoString を作成して関数の戻り値を保持し、それをスタック上の変数 'a' に渡す際に一時変数の演算子 T*() を呼び出し、次に GAutoPtr( T *ptr = 0) コンストラクター、コピー コンストラクターを使用する代わりに: GAutoPtr(AutoPtrRef p)

これにより、temp auto ptr がメモリを削除し、'a' が解放されたメモリへのポインタを保持します。

ただし、VC6 では、適切なコンストラクターを呼び出します。これをすべて言っていると、私は Linux と Mac でも gcc を使用しているので、私が書いたコードはそこでも動作する必要があります。VC2008 では、コピー コンストラクターで非 const by value 変数を使用できません。また、とにかく「const」は必要ありません。コピー コンストラクターがメモリ ブロックの所有権を取得し、コピー中のオブジェクトから所有権を削除するため、オブジェクトを変更するためです。

VC 2005/2008 でこれを機能させるにはどうすればよいですか?

4

2 に答える 2

1

これは最近の g++ でもコンパイルできましたか? MacBook Pro で試してみたところ、次のようになりました。

xxx.cpp: In function ‘AutoString func()’:
xxx.cpp:52: error: no matching function for call to ‘AutoPtr<char, true>::AutoPtr(AutoString)’
xxx.cpp:12: note: candidates are: AutoPtr<T, isArray>::AutoPtr(AutoPtr<T, isArray>&) [with T = char, bool isArray = true]
xxx.cpp:9: note:                 AutoPtr<T, isArray>::AutoPtr(T*) [with T = char, bool isArray = true]
xxx.cpp:52: error:   initializing temporary from result of ‘AutoPtr<T, isArray>::AutoPtr(T*) [with T = char, bool isArray = true]’

これをコンパイルできる唯一の方法は、コピーコンストラクターにconst参照を持たせることです。これは、私が疑っていたものです。Herb Sutter が最近の GotW で、これと非常によく似た内容について投稿したようです。std::auto_ptr非常に正当な理由がなければ、所有権の譲渡セマンティクスを複製しようとはしません。Boost.SmartPtrのさまざまな機能を確認することをお勧めします。可能であれば、代わりにそれらを使用してください。

何らかの理由でブーストできない場合は、お気に入りのstd::auto_ptr実装を読み、std::auto_ptr_refクラスに特に注意してください。なぜそこにあり、どのように機能するのかを正確に理解したら、戻って auto-ptr クラスを記述します。このクラスは、発生している問題を回避するために存在します。IIRC、これはJosuttis': The Standard C++ Libraryで詳細に説明されています。ここで初めて本当に理解できたと思います。

于 2009-04-14T02:52:26.597 に答える
1

T* 引数を取るコンストラクターを「explicit」キーワードでマークできます。これが機能し、偽の一時オブジェクトの作成も防止し、パフォーマンスが向上することを確認しました。ただし、そのコンストラクターを使用する人は、コンパイラーの型変換規則に頼ることができなくなりました。たとえば、関数は次のように変更されます。

GAutoString Func()
{
    char *s = new char[4];
    strcpy(s, "asd");
    return GAutoString(s);
}

面倒ですが、自動型変換は混乱を招く可能性があるため、この場合は必ずしも悪いことではないと思います。

于 2009-04-14T01:20:28.110 に答える