我按照以下方式组织了三个班级。 Foo
是一个模板类,Bar
来自Foo
,Doo
也来自Foo
。所有这些都实现了doX()
成员函数,该函数在Foo
中定义为虚函数。
我需要一个Bar
和Doo
个对象的向量(或任何其他容器)。例如,名为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;
}
答案 0 :(得分:3)
您已正确识别问题。您必须确保向量中指针指向的实例至少与向量本身一样长。您可以存储指向动态分配对象的指针,这意味着您负责控制其生命周期。这可以通过将指针存储到使用new
或更好的smart pointers创建的实例来实现。
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
答案 1 :(得分:2)
我不确定我是否理解你的问题,但是在执行以下操作时会遇到什么问题:
Foo<int> *x = new ...; // e.g. new Bar<int>()
target.push_back(x);
这将在堆上而不是堆栈中存储x的值。对象x
将一直存在,直到您明确删除if(delete x
)。当您不再需要该对象时,您必须调用delete x
,或者永远不会释放该部分内存,因此您将有内存泄漏。
答案 2 :(得分:1)
您将实例存储为向量中的值引用。这将使用隐式复制构造函数来构建Foo
和Bar
实例中的Doo
个对象。
您似乎希望将引用存储到对象中(正如您在示例的后面部分中所尝试的那样)。但是,为了做到这一点,你不能使用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;
}
答案 3 :(得分: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;
}
答案 4 :(得分:1)
对于多态对象的容器,您有两个选择(现在):
unique_ptr
:std::vector< std::unique_ptr<T> >
boost::ptr_vector<T>
我个人赞成这里的Boost Pointer Containers。它们是为此目的而制作的,并提供额外的保证(例如非无效性)和特定的糖涂层(取消引用迭代器会产生T&
,而不是再次需要取消引用的std::unique_ptr<T>&
。< / p>