在同一个向量中存储两个不同的类(具有相同的继承基类)? (没有提升)

时间:2011-12-21 07:27:41

标签: c++ class vector copy

我有两个不同的类(First,Second)继承了相同的基类(Base)。我想在同一个向量中存储First和Second的实例,而不将它们的类拼接到Base类。如果我使用向量,将发生这种拼接,如下所示:

#include <iostream>
#include <vector>

class Base {
  public:
    Base() { }
    virtual void test() { std::cout << "I am just the base class\n"; }
};

class First : public Base {
  public:
    First() { }
    void test() { std::cout << "This is the First class\n"; }
};

class Second : public Base {
  public:
    Second() { }
    void test() { std::cout << "This is the Second class\n"; }
};

int main() {
  First *f = new First();
  Second *s = new Second();

  // First, I keep a vector of pointers to their base class
  std::vector<Base> objs;
  objs.push_back(*f);
  objs.push_back(*s);
  objs[0].test();   // outputs "I am just the base class"
  objs[1].test();   // outputs "I am just the base class"
}

最终,当两个物体被放入矢量时,它们会被拼接。有没有办法(没有提升)将这两个对象复制到同一个向量中?矢量不是我想要的,我想复制对象。


这里有很多好的答案。不幸的是,我不能假设C ++ 11,所以我最终利用克隆构造函数来提示存储指针。但最终,克隆允许我首先创建副本。

这是我的最终解决方案:

#include <iostream>
#include <vector>

class Base {
  public:
    Base() { }
    virtual void test() { std::cout << "I am just the base class\n"; }
    virtual Base* clone() const = 0;
};

class First : public Base {
  public:
    First() { }
    void test() { std::cout << "This is the First class\n"; }
    First* clone() const { return new First(*this); }
};

class Second : public Base {
  public:
    Second() { }
    void test() { std::cout << "This is the Second class\n"; }
    Second* clone() const { return new Second(*this); }
};

int main() {
  First *f = new First();
  Second *s = new Second();

  std::vector<Base *> bak;
  bak.push_back(f->clone());

  bak[0]->test();

}

6 个答案:

答案 0 :(得分:3)

要复制继承的对象而不进行切片,您需要在基类中定义clone()函数并在派生类中重写:

class Base
{
public:
  Base* clone() const = 0;
}

class Derived
{  
  Derived* clone() const { return new Derived(*this); }
}

这样,即使使用指向基类的指针,您也将获得派生类的副本。

答案 1 :(得分:2)

是的,您创建了vector指向Base的指针:

std::vector<Base*> objs;

或者更好的是,基类的智能指针vector,所以你不必担心内存管理。

答案 2 :(得分:2)

我不知道你为什么要复制这些对象,但如果是为了便于内存管理,那么这样做:

std::vector<std::shared_ptr<Base> > objs;
objs.push_back(std::make_shared<First>());
objs.push_back(std::make_shared<Second>());

答案 3 :(得分:2)

顺便提一下你的问题,这是不可能的: vetor<Base>包含Base个序列,而FirstSecond都是Base加上更多内容。

就其本质而言,vector包含相同对象的序列(所有Base)和push_back,实际上并不将对象放在向量中,而是将对象复制到在向量末尾构造的对象(和是Base对象,因此只复制Base子组件。

要解决这个问题,你必须让你的向量不要“包含”Base - s,而是要“引用”Base-s。这需要指针

std::vector<Base*> v;
v.push_back(new First);
v.push_back(new Second);

但这也需要内存管理。这可以通过两种方式完成:

1)使用一些“拥有”基础的智能指针。 在C ++ 11中,有std::shared_ptrstd::unique_ptr。 在C ++ 03中,你必须安排一个像

这样的引用计数指针
template<class T>
class ptr
{
public:
  ptr() :p(), c() {}
  explicit ptr(T* z) :p(z), c(new unsigned(1)) {}
  ptr(const ptr& s) :p(s.p), c(s.c) { if(c) ++*c; }
  ptr& operator=(const ptr& s) 
  { 
    if(this!=&s) 
    { 
      if(c && !--*c) { delete p; delete c; }
      p = s.p; c=s.c;
      if(c) ++*c;
    }
    return *this;
  }
  ~ptr() { if(c && !--*c) { delete p; delete c; } }

  T* operator->() const { return p; }
  T& operator*() const { return *p; }

private:
  T* p;
  unsigned* c; 
};

并将您的矢量声明为

std::vector<ptr<Base> > v;
v.push_back(ptr(new First));
v.push_back(ptr(new Second));
v[0]->test();
v[1]->test();

销毁v,将破坏将反过来破坏对象的ptrs。 注意: Base还必须声明虚析构函数,否则派生对象不能被plymorphically销毁。

2) 或者,您可以“自定义”向量以“拥有”指针本身。 这可以通过

之类的东西获得
template<class T>
class owningvector: public std::vector<T*>
{
public:
  ~owningvector()
  {
    for(size_t i=0; i<size(); ++i)
      delete at(i);
  }
  owningvector() {}
private:
  //disable vector copy and assign
  owningvector(const owningvector&);
  owningvector& operator=(const owningvector&);
};

owningvector<Base> ov;
ov.push_back(new First);
ov.push_back(new Second);

注意: Base仍然需要虚拟析构函数。 此外,由于std::vectorownvector不是多态的破坏,不要将自己用作多态类型。

3) 如果你正在做on_stack的所有事情,你可以很容易地做到

int main()
{
  First first;
  Second second;
  std::vector<Base*> v;
  v.push_back(&first);
  v.push_back(&second);

  v[0]->test();
  v[1]->test();

  //no other memory management is required:
  // v is destroyed without doing anything special.
  // second and first are destroyed just further.
}

答案 4 :(得分:1)

定义复制构造函数,然后使用:

std::vector<Base*> objs;
objs.push_back(new First(*f));
objs.push_back(new Second(*s));

答案 5 :(得分:1)

我说你想要复制你的对象,我感到很沮丧。 通常,多态性和复制/分配不能很好地协同工作, 但你可以用letter-envelop习语来做到这一点。类似的东西:

class Base
{
    Base* myImpl;
    virtual Base* clone() const
    {
        //  or perhaps just abort, since this one shouldn't
        //  ever be called in practice.
        return new Base( myImpl->clone() );
    }
protected:
    Base() : myImpl( NULL ) {}
    Base( Base* impl ) : myImpl( impl ) {}

public:
    Base( Base const& other ) : myImpl( other.myImpl->clone() ) {}
    virtual ~Base() {}
    virtual void test() { myImpl->test(); }
};

class First : public Base
{
    virtual Base* clone() const
    {
        return new First( *this );
    }

    First() {}
public:
    static Base createInstance() { return Base( new First ); }
    virtual void test() { std::cout << "In First" << std::endl; }
};

class Second : public Base
{
    virtual Base* clone() const
    {
        return new Second( *this );
    }

    Second() {}
public:
    static Base createInstance() { return Base( new Second ); }
    virtual void test() { std::cout << "In Second" << std::endl; }
};

这导致类Base的行为多态,但是 支持复制和分配(并且分配可能会改变有效 类型),并不会切片。它还会导致严重的性能损失, 并且通常不是我想要的 - 在我的经验中,大多数 多态类具有标识,不应该是可复制的 转让。但它是一种可能的解决方案,并且在某些方面可能很有用 具体案例。