1

我按以下方式组织了三个班级。Foo是一个模板类,Bar派生自Foo,也Doo派生自Foo。它们都实现了一个doX()定义Foo为虚函数的成员函数。

我需要一个BarDoo对象的向量(或任何其他容器)。例如,一个包含两个名为 的对象的向量vec,第一个元素应该是 a Doo,第二个元素应该是 a 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函数中使用堆栈分配的对象,因为一旦函数返回,这些对象就会被释放。

解决此问题的一种方法是使用智能指针:

#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

由于理论已经解释过了,我再贴一个代码示例

#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 回答
1

对于多态对象的容器,您有两种选择(现在):

  • 使用 C++11:典型的 stl 容器,具有unique_ptrstd::vector< std::unique_ptr<T> >
  • 或者简单地说,Boost 指针容器:boost::ptr_vector<T>

我个人更喜欢这里的 Boost Pointer Containers。它们是为此目的而设计的,并提供额外的保证(例如非无效性)和特定的糖衣(取消引用迭代器会产生 a T&,而不是std::unique_ptr<T>&您需要再次取消引用的 a )。

于 2012-07-14T12:43:56.810 回答