Создание классов C с элементами, имеющими расширенные типы

#c

#c

Вопрос:

Как в C создать класс, члены которого являются типами, имеющими расширения? Возьмем пример car. У нас есть a Car и a BigCar . В Car комплекте идет an Engine , но BigCar должно быть a BigEngine . Но если мы просто скроем engine элемент BigCar with BigEngine , regular Engine все равно будет создан и может сделать что-то во время построения, чего мы не хотим. Есть ли лучший способ? Или это единственный способ «ничего не делать во время построения». Я знаю, что мы могли бы использовать DIC, но я думаю, что есть действительно очевидный способ, о котором я не думаю…

Пример

 #include <iostream>

class Engine {
public:
    Engine() {
        this->cylinders = 4;
    }
    virtual ~Engine() {}
    int cylinders;
};

class BigEngine : public Engine {
public:
    BigEngine():Engine() {
        this->cylinders = 8;
        this->fuel_injector = true;
    }
    bool fuel_injector;
};

class Car {
public:
    Car() {
        std::cout << this->engine.cylinders;
    }
    virtual ~Car(){ }
    Engine engine;
};

class BigCar : public Car {
public:
    BigCar():Car() {
        std::cout << this->engine.fuel_injector;
    }
    ~BigCar() { }
    BigEngine engine;
};

int main()
{
    BigCar car; // SHOULD print 81 but prints 41
}
  

Ответ №1:

С помощью шаблона:

 #include <iostream>

class Engine {
public:
    Engine() : cylinders{4} {}
    int cylinders;
};

class BigEngine : public Engine {
public:
    BigEngine() : Engine{}, fuel_injector{true} {
        this->cylinders = 8;
    }
    bool fuel_injector;
};

template<typename E = Engine>
class Car {
public:
    Car() {
        std::cout << this->engine.cylinders;
    }
    E engine;
};

class BigCar : public Car<BigEngine> {
public:
    BigCar(): Car<BigEngine>{} {
        std::cout << this->engine.fuel_injector;
    }
};


int main()
{
    BigCar car;
}
  

Однако проблема заключается в том, что не будет одного отдельного Car класса. Тем не менее, если вы хотите хранить разные типы движков в Car (типы с разными размерами), вам нужно вместо этого хранить указатели. Например:

 #include <iostream>
#include <memory>

class Engine {
public:
    Engine() : cylinders{4} {}
    int cylinders;
};

class BigEngine : public Engine {
public:
    BigEngine() : Engine{}, fuel_injector{true} {
        this->cylinders = 8;
    }
    bool fuel_injector;
};

class Car {
public:
    Car() : Car{std::make_unique<Engine>()} {
    }
    std::unique_ptr<Engine> engine;
protected:
    Car(std::unique_ptr<Engine> engine) : engine{std::move(engine)} {
        std::cout << this->engine->cylinders;
    }
};

class BigCar : public Car {
public:
    BigCar(): Car{std::make_unique<BigEngine>()} {
        std::cout << static_cast<BigEngine*>(this->engine.get())->fuel_injector;
    }
};


int main()
{
    BigCar car;
}
  

Ответ №2:

Ваш Car класс может иметь указатель на Engine , и соответствующий механизм может быть создан в constructo . Это позаботится о разных типах движков, когда вам придется добавлять электрические 🙂