2012-07-14 60 views
1

我有三個以下列方式組織的類。 Foo也是衍生自Foo的模板類別Bar和衍生自FooDoo。它們都實現了doX()成員函數,該函數在Foo中定義爲虛函數。繼承:包含派生類實例的基類的模板容器

我需要有一個向量(或任何其他容器)的BarDoo對象。例如,名爲vec的兩個對象的向量,第一個元素應該是Doo,第二個元素應該是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; 
} 

回答

3

您已正確識別該問題。您必須確保矢量中的指針指向的實例至少與矢量本身一樣長。您可以存儲指向動態分配對象的指針,這意味着您負責控制其生命週期。這可以通過存儲指向使用new創建的實例的指針或者更好的smart pointers來實現。

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

在上面的示例中,您必須確保在完成後(通常在向量超出範圍之前)刪除向量的元素。

A C++ 11例如用std::unique_ptrs

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 
+0

我之前沒有聽說過智能指針。上面的代碼現在解決了我的問題。我要去谷歌''標題。謝謝 – 2012-07-14 09:04:03

+1

@ sorush -r您可能需要C++ 11來處理'',但boost和TR1也提供了一些智能指針。 – juanchopanza 2012-07-14 09:06:33

2

我不知道如果我理解你的問題所在,但究竟是在做下列問題:

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

這將x的值存儲在堆上,而不是堆棧。對象x將一直存在,直到您明確刪除(如果是delete x)。當你不再需要這個對象時,你必須調用delete x,否則這部分內存將永遠不會被釋放,所以你將有內存泄漏。

1

要存儲你的實例爲矢量值的引用。這將使用隱式拷貝構造函數從BarDoo實例構造Foo對象。

您似乎希望將引用存儲到對象中(正如您在示例的後面部分中試圖做的那樣)。但是,爲了執行此操作,您不能使用doSomething函數中的堆棧分配對象,因爲函數返回後會將它們解除分配。要解決這個

的方法之一是使用智能指針:

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

對於多態性容器對象有兩種選擇(現在):

  • 用C++ 11:典型的stl容器,用unique_ptrstd::vector< std::unique_ptr<T> >
  • 或簡單地說,升壓指針容器:boost::ptr_vector<T>

我個人贊成加速指針容器在這裏。它們是爲此目的而製作的,並提供額外的保證(如非無效性)和特定的糖衣(取消引用迭代器會產生T&,而不是std::unique_ptr<T>&,您需要再次解除引用)。