Container of derived objects / smart pointers

2019-09-21 02:19发布

问题:

Lets say i have:

class Base {...};
class Derived1 : public Base {...};
class Derived2 : public Base {...};
class Derived3 : public Base {...};

class Store {
public:
    void Add(const Base&);    //Adds mix of Derived1/2/3
private:
    vector<const Base*> vec;    
    vector<shared_ptr<const Base> > vec_smart;
};

//------------------------------------------------------

void Store::Add(const Base& b){
    vec.push_back(&b);    //got sliced
}

When using vector of pointers to Base, it got sliced. I suppose i have to use smart pointers but I cant get it to work.

I tried something like:

auto tmp = make_shared<const Base> (b);
vec_smart.push_back(shared_ptr<const Base>(b));

But it still got sliced. What Iam doing wrong?

EDIT: I have << operator in Base class that calls virtual print in Derived classes.

ostream & operator <<(ostream & os, const Base & x) {
    x.print(os);
    return os;
}

void Base::print(ostream& os) const {
   os << "Base";
}

void Derived1::print(ostream& os) const {
   os << "Derived1";
}

When i call

cout << b;

In Store::Add it outputs fine but when I iterate over vector after it got stored all i get is "Base"

ostream & operator <<(ostream & os, const Store & x) {
    for (auto it = x.vec.begin(); it != x.vec.end(); ++it) {
        os << *(*it) << endl;
    }
    return os;
}

Is it a bad design?

回答1:

I am going to guess you didn't declare the relevant methods in Base as virtual. Try this:

struct Base
{
  virtual void print(std::ostream& os) const { os << "Base"; }
  virtual ~Base() {}
};

struct Derived1 : Base
{
  void print(std::ostream& os) const { os << "Derived1"; }
};

ans so on. Note I have only used struct here to save me typing public a few times.

Here is a working example:

#include <vector>
int main()
{
  Base b;
  Derived1 d;
  std::vector<const Base*> v{&b, &d};

  for (auto it = v.cbegin(); it != v.cend(); ++it)
  {
    std::cout << *(*it) << std::endl;
  }

}

Output:

Base
Derived1

You can see it in action here.

Edit Your code has undefined behaviour because you are storing pointers to temporaries:

Store a("aaaaaaa");
a.Add(Derived("abc", 5));
a.Add(Derived("def", 6));

This version works as expected:

Store a("aaaaaaa");
Derived d0("abc", 5);
Derived d1("def", 6);
a.Add(d0);
a.Add(d1);

Edit 2: from your comments:

Lets say Iam forced to use a.Add(Derived("abc", 5)); What can I do with it in my implementation?

You could make Add a template:

template <typename T>
void Add(const T& elem)
{
  vec.push_back(new T(elem));
}

or

template <typename T>
void Add(const T& elem)
{
  vec_smart.push_back(std::make_shared(elem));
}