Rick Anaconda
Rick Anaconda

Reputation: 65

Is there a way to copy a vector of derived class pointers without casting it to the base class?

I have 4 classes: 1 Base, 2 Deriveds and 1 Container class. The Container class holds a vector of Base pointers.

I want to create a copy constructor for my class Container that doesn't cast the Derived pointers to a Base, so that I can cast the Base pointers to Derived pointers afterwards.

class Base {
   int m_base_attribute;
public:
   Base() : m_base_attribute(420) {}

   virtual void say_hello() {
      std::cout << "Hello !" << std::endl;
   }
};

class Derived : public Base {
   int m_derived_attribute;
public:
   Derived() : Base(), m_derived_attribute(69) {}

   virtual void say_hello() {
      std::cout << "I'm a derived class !" << std::endl;
   }
};

class Container {
   std::vector<Base*> m_base_vector;
public:
   Container() {
      m_base_vector.push_back(new Derived());
   }

   Container(const Container& model) {
      for(auto base : model.m_base_vector){
         m_base_vector.push_back(new Base(*base));
      }
   }

   ~Container() {
      for(auto base : m_base_vector) {
         delete base;
      }
   }
};

Is there a way to do it without any memory leaks?

Upvotes: 0

Views: 77

Answers (1)

L. F.
L. F.

Reputation: 20579

The problem is that new Base(*base) always creates a Base object, never a Derived object. This is called slicing. The workaround is to use a virtual clone function and a virtual destructor:

class Base {
    int m_base_attribute;
public:
    // ...
    virtual std::unique_ptr<Base> clone() const
    {
        return std::make_unique<Base>(*this);
    }
    virtual ~Base() {}
};

class Derived : public Base {
    int m_derived_attribute;
public:
    // ...
    std::unique_ptr<Base> clone() const override
    {
        return std::make_unique<Derived>(*this);
    }
};

Note that I used std::unique_ptr instead of raw pointers to avoid memory leaks. Now you can implement the Container class without slicing:

class Container {
    std::vector<std::unique_ptr<Base>> m_base_vector;
public:
    // ...    
    Container(const Container& model)
    {
        m_base_vector.reserve(model.m_base_vector.size());
        for (const auto& p : m_base_vector) {
            m_base_vector.push_back(p->clone());
        }
    }
};

Upvotes: 2

Related Questions