14

自動ポインター クラスがあり、コンストラクターでポインターを渡しています。デストラクタで delete または delete[] を適切に呼び出せるように、コンストラクタで new と new[] を分離できるようにしたいと考えています。これは、テンプレートの特殊化を通じて行うことができますか? コンストラクターでブール値を渡す必要はありません。

    template <typename T>
    class MyAutoPtr
    {
    public:
      MyAutoPtr(T* aPtr);
    };

// in use:
MyAutoPtr<int> ptr(new int);
MyAutoPtr<int> ptr2(new int[10]);
4

7 に答える 7

7

残念だけど違う。どちらも同じ型を返しT*ます。オーバーロードされた適切なコンストラクターを呼び出すビルダー関数の使用を検討してください。

template <typename T>
class MyAutoPtr
{
public:
    MyAutoPtr(T* aPtr, bool array = false);
};

template <typename T>
MyAutoPtr<T> make_ptr() {
    return MyAutoPtr<T>(new T(), false);
}

template <typename T>
MyAutoPtr<T> make_ptr(size_t size) {
    return MyAutoPtr<T>(new T[size], true);
}

これで、次のようにオブジェクトをインスタンス化できます。

MyAutoPtr<int> ptr = make_ptr<int>();
MyAutoPtr<int> ptr2 = make_ptr<int>(10);
于 2010-04-07T08:21:32.870 に答える
3

std::unique_ptrC++0x では、以下に示すように、動的配列の特殊化が行われます。ただし、適切なインスタンスをインスタンス化するのはユーザーのタスクになります。言語レベルでは、あるポインターを別のポインターと区別する方法はありません。

template <class T>
class pointer
{
    T* p;
public:
    pointer(T* ptr = 0): p(ptr) {}
    ~pointer() { delete p; }
    //... rest of pointer interface
};

template <class T>
class pointer<T[]>
{
    T* p;
public:
    pointer(T* ptr = 0): p(ptr) {}
    ~pointer() { delete [] p; }
    //... rest of pointer and array interface
};

int main()
{
    pointer<int> single(new int);
    pointer<int[]> array(new int[10]);
}

さらに、1 つのクラスに非常に多くのタスクをロードするのはあまり良くないかもしれません。たとえば、ブーストにはshared_ptrとがありshared_arrayます。

于 2010-04-07T08:35:50.943 に答える
2

本当の解決策は、独自のオートポインター クラスを取り除き、C スタイルの配列の使用を取り除くことだと思います。これは以前から何度も何度も言われてきたことですが、C スタイルの配列を使用する意味はあまりありません。std::vectorそれらでできるほとんどすべてのことは、または を使用して行うことができますboost::array。そして、これらは両方とも異なる型を作成するため、それらをオーバーロードできます。

于 2010-04-07T08:44:16.333 に答える
2

一方、特定のmake機能を使用することもできます。

template <class T>
MyAutoPtr<T> make();

template <class T>
MyAutoPtr<T> make(size_t n);

もちろん、これは背後に適切なロジックがあることを意味しますが、カプセル化されています。T新しく作成されたポインターに渡されたオブジェクトをコピーするオーバーロードを追加することもできます。

最後に、コンストラクターのオーバーロードでも実行できます...ポイントは、new外部を呼び出さないことです。

于 2010-04-07T08:27:29.767 に答える
2

new int[X]配列の最初の要素へのポインターを生成するため、これは不可能です。と同じタイプint*です。

一般的な解決策の 1 つは、 deletersを使用することです。クラスにもう 1 つのテンプレート引数を追加して、ポインターにカスタムのデリータを渡すことができるようにします。それはあなたのクラスをより普遍的なものにします。次のようなデフォルトのデリータを作成できます。

struct default_deleter
{
    template<typename T>
    void operator()( T* aPtr ) { delete aPtr; }
};

配列の場合は、カスタムのデリータを渡すことができます:

struct array_deleter
{
    template<typename T>
    void operator()( T* aPtr ) { delete[] aPtr; }
};

最も単純な実装は次のとおりです。

template <typename T, typename D>
class MyAutoPtr
{
public: 
    MyAutoPtr(T* aPtr, D deleter = default_deleter() ) : ptr_(aPtr), deleter_(deleter) {};
    ~MyAutoPtr() { deleter_(ptr_); }
protected:
    D deleter_;
    T* ptr_;
};

次に、次のように使用できます。

MyAutoPtr<int, array_deleter> ptr2(new int[10], array_deleter() );

クラスをより複雑にして、deleter の型を推測できるようにすることができます。

于 2010-04-07T20:26:47.170 に答える
1

new[]配列からポインターへの暗黙的な変換が開始されるにもかかわらず、ポインター値を持つように明確に定義されています。

しかし、私はあなたが運が悪いとは思いません。結局のところ、あなたの例は へのポインタを管理しているintのではなく、 へのポインタを管理していますint[10]。だから理想的な方法は

MyAutoPtr<int[10]> ptr2(new int[10]);

Red-Nosed Unicorn が言及しているようにnew int[10]、C スタイルの配列は作成されません。コンパイラが C 標準にも準拠している場合はそうなりますが、C++ では C スタイルの配列を C の C スタイルの配列以上にすることができます。とにかく、new次のように尋ねると、C スタイルの配列が作成されます。

MyAutoPtr<int[10]> ptr2(new int [1] [10]);

残念ながら、delete contents;でも動作しませんint (*contents)[10];。コンパイラーは正しいことを行うことができます。標準では、配列が のようにポインターに変換されることを指定していませんnew。GCC が置換delete[]して警告を発したことを思い出すと思います。しかし、それは未定義の動作です。

そのため、呼び出すデストラクタと呼び出すデストラクタの 2 つが必要にdeleteなりますdelete[]。関数を部分的に特殊化することはできないため、機能には部分的に特殊化されたヘルパーが必要です

template< class T > struct smartptr_dtor {
    void operator()( T *ptr ) { delete ptr; }
};

template< class T, size_t N > struct smartptr_dtor< T[N] > {
    void operator()( T (*ptr) [N] ) { delete [] ptr; }
};

template< class T >
void proper_delete( T *p ) {
    smartptr_dtor< T >()( p );
}

何らかの理由で、私は自分自身に服従させました;v)

残念ながら、これは動的サイズの配列では機能しないため、別の回答を書きます。

于 2010-04-07T09:01:30.690 に答える
1

2 回目の試行…</p>

スマート ポインター クラスを配列についてスマートにするのは非常に簡単です。ご想像のとおり、最初から配列であることがわかっている場合は、ランタイム フラグやコンストラクターへの引数は必要ありません。唯一の問題は、newとのnew[]戻り値の型が同じであるため、この情報をスマート ポインター クラスに渡すことができないことです。

template< class T, bool is_array = false >
struct smartptr {
    T *storage;

    smartptr( T *in_st ) : storage( in_st ) {}

    ~smartptr() {
        if ( is_array ) delete [] storage; // one of these
        else delete storage; // is dead code, optimized out
    }
};

smartptr< int > sp( new int );
smartptr< int, true > sp2( new int[5] );

フラグに代わるものは、Visitorが C++0x で行うようboolに、の意味をオーバーロードすることです。T[]std::unique_ptr

template< class T >
struct smartptr {
    T *storage;

    smartptr( T *in_st ) : storage( in_st ) {}

    ~smartptr() { delete storage; }
};

template< class T > // partial specialization
struct smartptr< T [] > {
    T *storage; // "T[]" has nothing to do with storage or anything else

    smartptr( T *in_st ) : storage( in_st ) {}

    ~smartptr() { delete [] storage; }
};

smartptr< int > sp( new int );
smartptr< int[] > sp2( new int[5] );
于 2010-04-07T20:13:32.400 に答える