1

私は次のように編成された3つのクラスを持っています。Fooはテンプレート クラスであり、Barから派生しFoo、またからもDoo派生しFooます。それらはすべて、仮想関数として doX()定義されているメンバー関数を実装しています。Foo

オブジェクトのベクトル (またはその他のコンテナー) が必要Barです。Dooたとえば、 という名前の 2 つのオブジェクトのベクトルの場合vec、最初の要素は で、Doo2 番目の要素は である必要がありBarます。私が電話するときは、電話vec[0].doX() Doo::doX()する必要があります。オブジェクトへのポインタのベクトルを定義しFooて、仕事をしてください。しかし、インスタンスが実際にどこに保存されているかはわかりません。オブジェクトへのポインターを配置すると、オブジェクトが作成されたスコープを離れた後に、割り当てられたメモリが解放される場合があります。

問題を説明するための最小限の作業例を用意しました。

#include <iostream>
#include <vector>

using namespace std;

template <typename T>
class Foo
{
public:
    virtual void doX(){cout<<"doX from Foo"<<endl;}
};

template <typename T>
class Bar : public Foo<T>
{
public:
    void doX(){cout<<"doX from Bar"<<endl;}
    void g(string const &input);    // some test function may be used in doX
    int x;                          // some test data may be used in doX
};

template <typename T>
class Doo : public Foo<T>
{
public:
    void doX(){cout<<"doX from Doo"<<endl;}
};

void doSomething(vector<Foo<int>* >& target)
{
    Foo<int> X;
    // do some extreme job to generate X
    target.push_back(&X); // ==> This is problematic
}

int main()
{
    Foo<int> f;
    Bar<int> b;
    Doo<int> d;
    vector<Foo<int> > v;
    v.push_back(f);
    v.push_back(b);
    v.push_back(d);
    v[0].doX();             // doX from Foo
    v[1].doX();             // doX from Foo
    v[2].doX();             // doX from Foo
    vector<Foo<int>*> v2;
    v2.push_back(&f);       // here is no problem, but if `f` is generated inside
                            // a function that receives a reference to `vec` there
                            // will be problems
    doSomething(v2);        // This is problematic
    v2.push_back(&b);
    v2.push_back(&d);
    v2[0]->doX();           // doX from Foo
    v2[1]->doX();           // doX from Foo but dangerous! May crash
    v2[2]->doX();           // doX from Bar
    v2[3]->doX();           // doX from Doo

    return 0;
}
4

5 に答える 5

3

問題を正しく特定しました。ベクター内のポインターが指すインスタンスが、少なくともベクター自体と同じくらい生きていることを確認する必要があります。動的に割り当てられたオブジェクトへのポインターを格納できます。つまり、オブジェクトの有効期間を制御する必要があります。newこれは、 で作成されたインスタンスへのポインタを保存することで実現できます

void addElements(vector<Foo<int>* >& target)
{
    target.push_back(new Bar<int>());
    target.push_back(new Doo<int>());
}

上記の例では、完了したら (通常はベクトルが範囲外になる前に) ベクトルの要素を削除する必要があります。

std::unique_ptrsを使用した C++11 の例:

std::vector<std::unique_ptr<Foo<int>> v;
v.push_back(std::unique_ptr<Foo<int>>(new Bar<int>()); // moves the unique_ptr
v.push_back(std::unique_ptr<Foo<int>>(new Doo<int>()); // moves the unique_ptr
v.emplace_back(new Bar<int>()); // constructs the unique_ptr in place
于 2012-07-14T08:56:20.883 に答える
2

あなたの問題を正しく理解しているかどうかはわかりませんが、次のことを行う際の問題は何ですか:

Foo<int> *x = new ...; // e.g. new Bar<int>()
target.push_back(x);

これにより、x の値がスタックではなくヒープに格納されます。オブジェクトxは明示的に削除するまで存続します if (with delete x)。オブジェクトが不要になった場合は、 を呼び出す必要があります。そうしないとdelete x、メモリのその部分が解放されないため、メモリ リークが発生します。

于 2012-07-14T08:51:56.047 に答える
1

インスタンスを値参照としてベクターに保存しています。これは、暗黙的なコピー コンストラクターを使用して、インスタンスFooからオブジェクトを構築します。BarDoo

代わりにオブジェクトへの参照を保存しようとしているようです (例の後半で試みたように)。ただし、これを行うために、関数から割り当てられたスタック オブジェクトを使用することはできませんdoSomething。これらは、関数が戻ると割り当てが解除されるためです。

これを解決する 1 つの方法は、スマート ポインターを使用することです。

#include <iostream>
#include <vector>
#include <tr1/memory> // assuming g++ now

using namespace std;
using namespace std::tr1;

template <typename T>
class Foo
{
public:
    virtual ~Foo(){}
    virtual void doX(){cout<<"doX from Foo"<<endl;}
};

template <typename T>
class Bar : public Foo<T>
{
public:
    void doX(){cout<<"doX from Bar"<<endl;}
    void g(string const &input);    // some test function may be used in doX
    int x;                          // some test data may be used in doX
};

template <typename T>
class Doo : public Foo<T>
{
public:
    void doX(){cout<<"doX from Doo"<<endl;}
};

void doSomething(vector<shared_ptr<Foo<int> > >& target)
{
    Foo<int> X;
    // do some extreme job to generate X
    shared_ptr<Foo<int> > foo(new Foo<int>);
    target.push_back(foo);
}

int main()
{
    Foo<int> f;
    Bar<int> b;
    Doo<int> d;
    vector<Foo<int> > v;
    v.push_back(f);
    v.push_back(b);
    v.push_back(d);
    v[0].doX();             // doX from Foo
    v[1].doX();             // doX from Foo
    v[2].doX();             // doX from Foo
    vector<shared_ptr<Foo<int> > > v2;
    v2.push_back(shared_ptr<Foo<int> >(new Foo<int>));
    doSomething(v2);
    v2.push_back(shared_ptr<Foo<int> >(new Bar<int>));
    v2.push_back(shared_ptr<Foo<int> >(new Doo<int>));
    v2[0]->doX();           // doX from Foo
    v2[1]->doX();           // doX from Foo
    v2[2]->doX();           // doX from Bar
    v2[3]->doX();           // doX from Doo

    return 0;
}
于 2012-07-14T09:05:58.997 に答える
1

ポリモーフィック オブジェクトのコンテナーの場合、2 つの選択肢があります (現在)。

  • unique_ptrC++11 の場合:典型的な stl コンテナー。std::vector< std::unique_ptr<T> >
  • または単にブースト ポインター コンテナー:boost::ptr_vector<T>

個人的には、Boost Pointer Containers がおすすめです。これらはこの目的のために作成されており、追加の保証 (null でないなど) と特定のシュガー コーティング (イテレータを逆参照すると、もう一度逆参照する必要があるT&ではなく、が生成されます) を提供します。std::unique_ptr<T>&

于 2012-07-14T12:43:56.810 に答える
1

理論はすでに説明されているので、コード例をもう 1 つ投稿します。

#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>

using namespace std;

template <typename T>
class Foo
{
public:
    virtual void doX(){cout<<"doX from Foo"<<endl;}
};


template <typename T>
class Bar : public Foo<T>
{
public:
    void doX(){cout<<"doX from Bar"<<endl;}
    void g(string const &input);   
    int x;                        
};

template <typename T>
class Doo : public Foo<T>
{
public:
    void doX(){cout<<"doX from Doo"<<endl;}
};

typedef boost::shared_ptr<Foo<int> >Ptr;

void doSomething(vector<Ptr>& target)
{
    target.push_back(Ptr(new Foo<int>));
}

int main()
{
    Foo<int> f;
    Bar<int> b;
    Doo<int> d;

    vector<Ptr> v;

    v.push_back(Ptr(new Foo<int>()));
    doSomething(v2); 
    v.push_back(Ptr(new Bar<int>(b)));
    v.push_back(Ptr(new Doo<int>(d)));

    v[0]->doX();         
    v[1]->doX();        
    v[2]->doX();       
    v[3]->doX();      

    return 0;
}
于 2012-07-14T09:22:21.377 に答える