简体   繁体   中英

How to have base abstract class object as class member but accept derived class memebers in constructor?

Setup

I've encountered the following situation in my C++ code (this is an example to illustrate the problem, and has not much to do with the actual code). I have a virtual class Family and two classes that I derive from it:

class Family {
    public:
        virtual double operator()(double const & x) const = 0;
        virtual ~Family();
};


class Mother : public Family {
    public:
        double operator()(double const & x) const override { return x*x; }
};

class Father : public Family {
    public:
        double operator()(double const & x) const override { return x-2; }
};

Then I have another class Car . This class should have a private member that can be either an object from the Mother or Father class. I tried to implement this as

class Car {
    public:
        Car(Family member) : right_seat_(member)  {}
    private:
        Family right_seat_;
};

If one tries to run the above via the main function

int main(){
    Mother lucy = Mother();
    Car Van = Car(lucy);
}

I get the error that member in the constructor of Car cannot be declared since Family is abstract. I understand the problem and why this happens, but I don't know what the right tool in C++ is to solve the problem. Unfortunately I also don't know how to google this properly (I didn't really find any suitable suggestions).


What I tried so far

The only idea that I had was to remove the abstract class altogether and template the Car class. I'd like to avoid this, since in the original problem the two derived classes logically belong to a "superclass", so I don't want to introduce this split if it is not absolutely necessary.

You need to use references or pointers for polymorphism. One possible solution would be:

class Car {
    public:
        Car(Family* member) : right_seat_(member)  {}
    private:
        Family* right_seat_;
};
int main(){
    Mother* lucy = new Mother();
    Car Van = Car(lucy);
    ...//do stuff with car
}

Don't forget to delete the pointers once you are done with them.
If you can use c++11 (c++14 for std::make_unique) or higher, using smart-pointers is even better:

class Car {
    public:
        Car(std::unique_ptr<Family>&& member) : right_seat_(std::move(member))  {}
    private:
        std::unique_ptr<Family> right_seat_;
};
int main(){
    std::unique_ptr<Family> lucy = std::make_unique<Mother>();
    Car Van = Car(std:move(lucy));
    ...//do stuff with car
}

Abstract classes cannot be members because they cannot be instantiated.

Dynamic polymorphism requires indirection. What you can have is a pointer to an abstract base.

This class should have a private member that can be either an object from the Mother or Father class.

A variable in C++ cannot have sometimes one type and other times another type. There is exactly on type for every variable except within templates where different instances of a template can have different types.

Again, this is where you need indirection: You can have a pointer that points to a base, and it doesn't matter which derived class contains that base.

If you want Car to own (ie control and be responsible for the lifetime of) the indirectly referred family as it would own a member, then you need to use dynamic allocation. That said, such ownership relationship seems dubious from design perspective.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM