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

Posted

技术标签:

【中文标题】在同一个向量中存储两个不同的类(具有相同的继承基类)? (没有提升)【英文标题】:store two different classes (with same inherited base class) in same vector? (no boost) 【发布时间】:2011-12-21 07:27:41 【问题描述】:

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

#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"

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


这里有很多很好的答案。不幸的是,我不能假设 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();


【问题讨论】:

我认为您的意思是“切片”。 “拼接”正好相反。 请确保在使用不带智能指针的 clone() 时,删除您克隆的所有内容 【参考方案1】:

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

class Base

public:
  Base* clone() const = 0;


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

这样,即使有一个指向基类的指针,你也会得到一个派生类的副本。

【讨论】:

【参考方案2】:

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

std::vector<Base*> objs;

或者,更好的是,vector 指向基类的智能指针,因此您无需担心内存管理。

【讨论】:

我正在寻找复制对象,抱歉没有澄清。 @gnychis 我不明白。如果要复制对象,请使用复制构造函数... @gnychis:然后深度复制指针。否则无法完成【参考方案3】:

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

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

【讨论】:

注意:std::make_shared 是 c++11,在 c++03 中不可用 @mark: std::shared_ptr 在 C++03 中也不可用。关键是OP可以用智能指针解决他的问题。智能指针的命名空间无关紧要。他也可以使用boost:: @sad_man:OP 将其帖子命名为“(无提升)”。不,C++03 中没有 std::shared_ptr (除了一些实验性扩展:shared_ptr 在 C++11 中已被批准为“标准”,主要是通过使用 C++11 特定功能重写 boost::shared_ptr) 【参考方案4】:

按照您提出问题的方式,这是不可能的: vetor&lt;Base&gt; 包含 Bases 序列,而 FirstSecond 都是 Base 以及更多内容。

就其本质而言,vector 包含一系列相同的对象(所有 Base)和 push_back,实际上并没有将对象放在 vector 中,而是将对象复制到在 vector 末尾构造的那个中(并且是一个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,会销毁ptr,而ptr又会销毁对象。 注意: Base 还必须声明一个虚拟析构函数,否则派生对象不能被多态销毁。

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.

【讨论】:

【参考方案5】:

重新定义复制构造器,然后使用:

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

【讨论】:

【参考方案6】:

你说你想复制你的对象,我很感动。 通常,多态性和复制/赋值不能很好地协同工作, 但是您可以使用信封成语来做到这一点。类似的东西:

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,它的行为是多态的,但是 支持复制和分配(并且分配可能会改变有效 类型),并且不会切片。它还会导致严重的性能损失, 并且通常不是想要的——根据我的经验,大多数 多态类有身份,不应该是可复制的或 可分配的。但这是一个可能的解决方案,并且在某些情况下可能有用 具体案例。

【讨论】:

以上是关于在同一个向量中存储两个不同的类(具有相同的继承基类)? (没有提升)的主要内容,如果未能解决你的问题,请参考以下文章

关于继承和覆盖的问题

将继承的类对象的向量传递给期望基类向量的函数

面向对象之继承

将对象存储在向量中,编译器说元素不存在

创建两组具有不同继承的类

有没有办法将两个或多个不同的类链接到一个类中(然后在向量上使用)?