С++: динамически выбирать, какой подкласс создавать

Я новичок в С++ и у меня есть вопрос.

Допустим, у нас есть базовый класс Base и два производных класса, Derived1 и Derived2. например Derived1 имеет конструктор, принимающий целое число, а Derived2 конструктор, принимающий логическое значение.

Можно ли определить во время выполнения (или во время компиляции), какой из этих двух подклассов создать, и назначить его базовому классу.

Что-то вроде этого: Base b = ???(значение), где значение имеет целочисленный или логический тип.

Заранее спасибо!


person George    schedule 24.01.2010    source источник
comment
Если вы привыкли к Java, помните, что в C++ Base b объявляет объект, а не ссылку на объект. Если вы назначаете производный объект базовому объекту, он будет нарезан (при условии, что он даже компилируется). Для динамического полиморфизма в С++ вам нужно использовать указатели, как в ответе Арака.   -  person Steve Jessop    schedule 24.01.2010
comment
Чем занимаются ваши занятия? Возможно, было бы разумнее использовать производный шаблон, например, что-то вроде Derived<int> d(someInt);.   -  person Georg Fritzsche    schedule 24.01.2010


Ответы (3)


Вероятно, вам нужен шаблон проектирования Factory.

person Nikolai Fetissov    schedule 24.01.2010

Напишите две перегрузки функции с именем createMyBlaBla. Один принимает int, а другой принимает bool. Каждый возвращает желаемый тип производного класса. например.:

Base* create(int n)
{
    return new Derived1(n);
}
Base* create(bool b)
{
    return new Derived2(b);
}
....
Base* b1 = create(10);    // Derived1
Base* b2 = create(false); // Derived2

Люди называют это фабричным шаблоном.

person AraK    schedule 24.01.2010

Я действительно не думаю, что это возможно так, как вы хотите, полиморфизм в C++ просто не работает так.

Если я правильно понял, вам нужна переменная, объявленная как Base, которая решает, в зависимости от типа параметра, будет ли она Derived1 или Derived2, и все это без использования шаблона Factory.

Причина, по которой это невозможно, заключается в том, что класс Base на самом деле не знает о существовании своих классов Derived, и вы не можете объявить переменную стека и заставить ее «вести себя» как производный класс. Тем не менее, я могу предложить обходной путь, но опять же, это не удовлетворяет всем ожиданиям реальной иерархии классов, которую вы хотите (если вы действительно этого хотите)

class Facade{

public:
    Facade(int foo) : b(new Derived1(foo)){}

    Facade(bool foo) : b(new Derived2(foo)){}

    Base Value()
    {
        return *b;
    }

private:
    Base* b;

};

И тогда вы можете сделать что-то вроде:

Facade foo(10);
Facade bar(true);

int x = (reinterpret_cast<Derived1*>(foo.Value())) -> val;
bool y = (reinterpret_cast<Derived2*>(bar.Value())) -> val;
person Anzurio    schedule 24.01.2010
comment
Кстати, reinterpret_cast небезопасен... Вместо этого лучше использовать dynamic_cast. - person Anzurio; 24.01.2010
comment
Downcast должен использовать static_cast, если вызывающая сторона уверена, что тип среды выполнения на самом деле является указанным производным типом, в противном случае — dynamic_cast (и проверять наличие null). Вы можете использовать классы dynamic_cast только с виртуальной функцией, поэтому убедитесь, что она есть (вероятно, деструктор будет виртуальным, если вы возитесь с указателями и полиморфизмом). - person Steve Jessop; 24.01.2010