在 C++ 中具有继承的对象向量

Posted

技术标签:

【中文标题】在 C++ 中具有继承的对象向量【英文标题】:vector of objects with inheritance in C++ 【发布时间】:2011-02-17 11:52:20 【问题描述】:
class IFeature

public:
  virtual std::string string() = 0;
;

class Feature2D

public:
  virtual std::string string()  .... 
;

class Feature3D

public:
  virtual std::string string()  .... 
;

void print(std::vector<IFeature*> & v)

  for (size_t i=0; i<v.size(); i++)
    std::cout << v[i]->string() << std::endl;


void main()

  std::vector<Feature2D*> v2d;
  // push...
  print(v2d); // compile error 

  std::vector<Feature3D*> v3d;
  // push...
  print(v3d); // compile error 

关于如何获得此打印功能的任何建议? (可能使用与 std::vector 不同的另一个数据结构)

谢谢

【问题讨论】:

您可以(并且应该)通过 const 引用将您的向量传递给print。此外,FeatureXD 类声明中缺少继承语句。 @Stefan - 请不要建议对问题中发布的代码进行编辑。很可能任何错误都是 OP 看到的问题的原因 是的,当然,我在想什么 :) 【参考方案1】:

使用模板。

template<typename T> void print(std::vector<T *> const & v) 
  for (size_t i=0; i<v.size(); i++)
    std::cout << v[i]->string() << std::endl;

或者,使用虚拟打印成员函数:

class IFeature

public:
  virtual std::string string() = 0;
  virtual void print(std::ostream & Dest) const = 0;
;

void print(std::vector<IFeature *> const & v) 
  for (size_t i=0; i<v.size(); i++) 
    v[i]->print(cout);
    cout << endl;
  

可选地与操作符组合

inline std::ostream & operator<<(std::ostream & Dest, IFeature const & v) 
  v.print(Dest);
  return Dest;

void print(std::vector<IFeature *> const & v) 
  for (size_t i=0; i<v.size(); i++)
    std::cout << *(v[i]) << std::endl;

【讨论】:

非常感谢!那个“const”是干什么用的? 表示打印不会修改v。【参考方案2】:

只需制作向量 IFeature* -向量。您可以在其中存储继承类的指针。

std::vector<IFeature*> v2d;
v2d.push_back(new Feature2D());
print(v2d);

无需使用模板。当您需要访问常见的虚函数时,可以使用指向超类的指针。这样你也可以在同一个向量中混合不同的子类:

std::vector<IFeature*> vMixed;
vMixed.push_back(new Feature2D());
vMixed.push_back(new Feature3D());
print(vMixed);

当然,如果您还需要继承类的指针,事情就会变得有点棘手。一种选择是将它们单独存储在其他地方。你也可以低调,但这通常是不推荐的。

【讨论】:

但是,如果您需要通过向量访问继承的类,这将不起作用。 (你需要丑陋的沮丧。)【参考方案3】:

为了完整起见,我将补充一点,您可以重新解释转换向量,因为所有向量共享相同的二进制代码。

print(reinterpret_cast<std::vector<IFeature*>&>(v2d));

C++ 没有模板参数的协方差,但如果你知道它在底层做了什么,你可以部分模拟它。我发现 reinterpret_cast 对于将 vector&lt;T*&gt;&amp; 转换为 vector&lt;const T*&gt;&amp; 以实现逆变也很有用。

当然,这很丑。

【讨论】:

【参考方案4】:

您可以将print 本身制作成模板:

template<typename T>
void print(T const &v)

  for (size_t i=0; i<v.size(); i++)
    std::cout << v[i]->string() << std::endl;

更好的是,使用迭代器,然后它也可以在大多数其他标准容器上工作:

template<typename T>
void print(T const &v)

  for (T::const_iterator i = v.begin(); i != v.end(); ++i)
    std::cout << (*i)->string() << std::endl;

更好(感谢 Pedro),自己传递迭代器:

template<typename Iter>
void print(Iter begin, Iter end) 
  for (Iter i = begin; i != end; ++i)
    std::cout << (*i)->string() << std::endl;

【讨论】:

我会更进一步,建议您对迭代器进行操作,而不是对 T 的 const 引用进行操作。所以:template void print(InputIterator begin, InputIterator end) ... 【参考方案5】:

您在这里寻找的是接口协方差,这(据我所知)在 C++ 类上是不可能的。您还需要将 print 设为模板函数(将 IFeature* 替换为 T*)。

【讨论】:

以上是关于在 C++ 中具有继承的对象向量的主要内容,如果未能解决你的问题,请参考以下文章

(C++ 继承)在 stl 容器中存储具有共同父对象的对象

从继承的对象中检索信息

关于具有多个类和标头的运输程序中的继承、隐私和对象的 C++ 问题

C++:多态公有继承中的虚方法

C++入门到精通:面向对象程序设计中的继承与派生!

通过 void 指针通过 C ABI 传递 C++ 对象(可能具有多重虚拟继承)