Как иметь объект базового абстрактного класса в качестве члена класса, но принимать членов производного класса в конструкторе? - PullRequest
0 голосов
/ 06 мая 2020

Setup

Я столкнулся со следующей ситуацией в моем коде C ++ (это пример для иллюстрации проблемы и не имеет ничего общего с реальным кодом). У меня есть виртуальный класс Family и два класса, которые я получаю от него:

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; }
};

Затем у меня есть еще один класс Car. Этот класс должен иметь закрытый член, который может быть объектом из класса Mother или Father. Я попытался реализовать это как

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

Если кто-то попытается запустить указанное выше через основную функцию

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

, я получаю сообщение об ошибке member в конструкторе Car не может быть объявлен, поскольку Family является абстрактным. Я понимаю проблему и почему это происходит, но я не знаю, какой инструмент в C ++ лучше всего подходит для ее решения. К сожалению, я также не знаю, как это правильно гуглить (на самом деле я не нашел подходящих предложений).

То, что я пробовал до сих пор

Единственная идея, которая у меня была, заключалась в том, чтобы полностью удалить абстрактный класс и создать шаблон для класса Car. Я бы хотел избежать этого, поскольку в исходной задаче два производных класса логически принадлежат к «суперклассу», поэтому я не хочу вводить это разделение, если оно не является абсолютно необходимым.

Ответы [ 2 ]

2 голосов
/ 06 мая 2020

Вам нужно использовать ссылки или указатели для полиморфизма. Одно из возможных решений:

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
}

Не забудьте delete указатели, как только вы закончите с ними.
Если вы можете использовать c ++ 11 (c ++ 14 для std :: make_unique) или выше, использование смарт-указателей еще лучше:

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
}
1 голос
/ 06 мая 2020

Абстрактные классы не могут быть членами, потому что они не могут быть созданы.

Dynami c полиморфизм требует косвенного обращения. У вас может быть указатель на абстрактную базу.

Этот класс должен иметь закрытый член, который может быть объектом из класса Mother или Father.

Переменная в C ++ иногда не может иметь один тип, а иногда - другой. Для каждой переменной существует точно один тип, за исключением шаблонов, где разные экземпляры шаблона могут иметь разные типы.

Опять же, здесь вам нужна косвенная ссылка: у вас может быть указатель, указывающий на базу, и он не имеет значения, какой производный класс содержит эту базу.

Если вы хотите, чтобы Car владел (т. е. контролировал и отвечал за время жизни) косвенно упомянутым семейством, поскольку он будет владеть членом, тогда вам понадобится использовать динамическое выделение c. Тем не менее, такие отношения собственности кажутся сомнительными с точки зрения дизайна.

...