c++pointerspolymorphismraiicopy-assignment

member vector with base class pointers


I have a class with a vector as below:

#include <vector>

class Base{};

class Derived: public Base{};

class Foo{
   private:
        std::vector<Base*> vec;
   public:
        Foo() = default;
        void addObject(const Base* b){
        // vec.push_back(new Base(*b));
        // vec.push_back(new Derived(*b));
        }
};

int main(){
        Derived* d = new Derived();
        Base* b = new Base();
        Foo f;
        f.addObject(d);
        f.addObject(b);
        
        delete d;
        delete b;
        return 0;
}

The function addBase may receive Derived's pointers. The line vec.push_back(new Base(b)); is expected to use b's copy to initialize a new object whose pointer will be pushed_back. If I don't use new, I will have resources shared between b and the vector(This is a sin).

I want to maintain polymorphism. How do I ensure objects that are pushed back maintain the type they were assigned during their creation without forcing everything into a Base object.


Solution

  • If you want to create a clone of the object passed to addObject, you have essentially two options. 1) Have a virtual function clone in Base and Derived, or better 2) let the copy constructor handle it.

    If you go for the second option, addObject need to the know the actual type of the passed object. This could be done with a template:

    template <class T>
    void addObject(const T * t)
    {
      Base *b = new T(*t);
    }
    

    In a robust program we don't want to use new/delete but rather std::unique_ptr or std::shared_ptr. Passing t as a reference, rater than pointer, is also considered a better practice.

    #include <vector>
    #include <memory>
    #include <iostream>
    
    
    class Base
    {
    public:
      virtual void cname() { std::cout << "Base" << std::endl;}
    };
    
    
    class Derived: public Base
    {
    public:
      virtual void cname() { std::cout << "Derived" << std::endl;}
    };
     
    
    class Foo
    {
    private:
      // You may switch to `std::shared_ptr` if that better fits your use case
      std::vector<std::unique_ptr<Base> > vec;
    public:
      template <class T>
      void addObject(const T & t)
      {
        vec.push_back(std::make_unique<T> (t)); 
      }
    
      void dump()
      {
        for (auto &bp: vec)
          bp->cname();
      }
    };
    
    int main(){
      Derived d;
      Base b;
    
      Foo f;
      f.addObject(d);
      f.addObject(b);
    
      f.dump();
      
      return 0;
    }
    

    Naturally for this to work, the reference passed to addObject must be of the correct type. This will create a clone of type Base, not Derived:

    Derived d;
    Base &e = d;
    f.addObject(e);