开发者

vector of objects with inheritance in C++

class IFeature
{
public:
  virtual std::string string() = 0;
};

cla开发者_如何学编程ss 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 
}

Any suggestions on how I can obtain this print function? (maybe using another data structure different by std::vector)

Thanks


Use a template.

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;
}

Or, use a virtual print member function:

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;
  }
}

Optionally combine with an operator<<

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;
}


Just make the vectors IFeature* -vectors. You can store pointers for inherited classes in them just fine.

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

No need to use templates. Pointers to the superclass are the way to go when you need to access common virtual functions. This way you can also mix different subclasses inside the same vector:

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

Of course, if you also need pointers for the inherited classes, things get a bit more tricky. One option is to store them elsewhere separately. You can also downcast, but that is not usually recommendable.


For the sake of completeness, I'll add that you can reinterpret_cast the vectors, since all vector share the same binary code.

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

C++ doesn't have covariance for template parameters, but you can simulate it partly if you know what it does under the hood. I found reinterpret_cast to be also useful to convert vector<T*>& to vector<const T*>& for contravariance.

Granted, this is very ugly.


You can make print itself into a template:

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

Better yet, use iterators, then it'll work on most other standard containers as well:

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;
}

Even better (thanks Pedro), pass the iterators themselves:

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


What you are looking for here is interface covariance, which (as far as I know) is not possible on C++ classes. You need to make print also a Templated function (replace IFeature* with T*).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜