0

I'm a bit stumped about how you would achieve something like that in C++. I have the feeling it might have to do with my architecture.
Let's say I have a a garage in Montreal and in this garage, there are different cars.I would usually go with a vector of pointers to base class Car here. Here is the problem though, I then want to deep copy the garage and open one in Miami. Futhermore, the Saab cars needs to have a different copy constructor, but with the same signature. There will be other exceptions like this down the road. I don't know what's in the Garage beforehand. Here is the code I have so far.

        //base class
        class Car
        {
            public:

                Car(int speed);

            private:
                int speed;

        }
        class Garage
        {
               public:
                      Garage(){};
                      //copy constructor of the garage
                      Garage(const Garage &toCopy)
                      {
                         for(int i=0;i<toCopy.cars.size();i++)
                         {
                         ReceiveCar(*cars[i]);
                         }
                      }
                      vector<std::shared<Car>> cars;
                      template <typename T>
                      T* ReceiveCar()
                      {
                         std::shared_ptr<T> ptr = std::make_shared<T>();
                         cars.push_back(ptr);
                         return ptr.get();
                      }
                      T* ReceiveCar(Car &toCopy)
                      {
                         std::shared_ptr<T> ptr = std::make_shared<T>(toCopy)                     cars.push_back(ptr);
                         return ptr.get();
                       }

            }
      class Saab : public Car
    {
        public:
              Saab(int speed)
                :Car(speed)
              {
                 DoWork();
              };
    }
    class AstonMartin: public Car
    {
        public:
              AstonMartin(int speed)
                  :Car(speed)
              {
                 DoImportantStuff();
              };
              AstonMartin(const AstonMartin& toCopy)
                  :Car(toCopy.speed)
              {
                 DoImportantStuff();
              };
    }  

Of course, the copy constructor used is the implicit one from Car. Is it possible to call the derived class' one instead without dynamic casting between ifs/elses for every exception to the rule there is. Maybe store each derived classes with the pointer somehow?
PS:I know I should not be returning raw pointers, but the destructors for the cars are private in my code, so the shared_pointers are still safe.

miniconco
  • 55
  • 1
  • 5

2 Answers2

2

The usual solution to this is to have a virtual clone() method that you call to make a copy of an object rather than using the copy constructor.

mattnewport
  • 13,728
  • 2
  • 35
  • 39
1

In context of inheritance a copy constructor should be designed as a so called “virtual copy constructor”. These functions are called often copySelf, cloneSelf or simply clone.

Here is Your elaborated example using copy constructors and a virtual copy constructor:

#include <memory>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;

//base class
class Car
{ public:
    Car(int speed): speed(speed) {};
    Car(const Car& c): speed(c.speed)
    { cout <<"Copy Constructor Car" <<endl;
    }
    virtual void display()
    { cout <<" speed: " <<speed <<endl;
    }
    virtual Car* clone()const =0;  // virtual copy constructor
  private:
    int speed;
};
typedef std::shared_ptr<Car> CarP;
class Garage
{ private:
    vector<CarP> cars;
  public:
    Garage() {}
    //copy constructor of the garage
    Garage(const Garage &toCopy)
    { cout <<"Copy Constructor Garage" <<endl;
      for (CarP c : toCopy.cars)
      { CarP cp(c->clone());
        ReceiveCar(cp);
      }
    }
    void ReceiveCar(CarP toCopy)
    { cars.push_back(toCopy);
    }
    void display()
    { for (CarP c : cars)  c->display();
    }
};
class Saab: public Car
{ public:
    Saab(int speed): Car(speed)
    { cout <<"Constructor Saab" <<endl;
    }
    void display()
    { cout <<"Car: Saab";  Car::display();
    }
    Saab* clone()const
    { return new Saab(*this);
    }
};
class AstonMartin: public Car
{ public:
    AstonMartin(int speed): Car(speed)
    { cout <<"Constructor AstonMartin" <<endl;
    }
    AstonMartin(const AstonMartin& toCopy): Car(toCopy)
    { cout <<"Copy Constructor AstonMartin" <<endl;
    }
    void display()
    { cout <<"Car: AstonMartin";  Car::display();
    }
    AstonMartin* clone()const
    { return new AstonMartin(*this);
    }
};
int main()
{
  CarP saab  =std::make_shared <Saab>(100);
  CarP aston =std::make_shared <AstonMartin>(200);
  Garage montreal;
  montreal.ReceiveCar(saab);
  montreal.ReceiveCar(aston);
  montreal.display();
  Garage miami(montreal);  // your decision to offer the user a copy constructor:
  miami.display();
}

produced output:

Constructor Saab
Constructor AstonMartin
Car: Saab speed: 100
Car: AstonMartin speed: 200
Copy Constructor Garage
Copy Constructor Car
Copy Constructor Car
Copy Constructor AstonMartin
Car: Saab speed: 100
Car: AstonMartin speed: 200
Roland
  • 336
  • 2
  • 8