#c #c 17 #covariant-return-types
#c #c 17 #ковариантные типы возврата
Вопрос:
Я создаю два простых класса по наследованию и добавляю виртуальную функцию и переопределение в дочернем классе.
class Base { public: virtual Base* getThis() { std::cout lt;lt; "called Base::getThis()n"; return this; } virtual void func1() { std::cout lt;lt; "called Base::func1n"; } }; class Derived : public Base { public: Derived* getThis() override { std::cout lt;lt; "called Derived::getThis()n"; return this; } void func1() override { std::cout lt;lt; "called Derived::func1n"; } void func2() { std::cout lt;lt; "called Derived::func2n"; } };
С помощью ковариации c я могу это сделать:
int main() { Derived d{}; Base* b{ amp;d }; b-gt;getThis()-gt;func1(); return 0; }
А теперь я хотел бы позвонить Derived::func2
.
b-gt;getThis()-gt;func2();
Предыдущий код выдает следующую ошибку: error: 'class Base' has no member named 'func2';
Единственное решение, которое у меня есть:
dynamic_castlt;Derived*gt;(b-gt;getThis())-gt;func2();
Мы должны использовать dynamic_cast
или static_cast
, позвонить Derived::func2
?
Есть ли другой метод?
Комментарии:
1. С точки зрения ООП это довольно подозрительно , что, как только вы «понизили» тип указателя с
Derived*
доBase*
, вы хотите получить доступ к элементам, специфичным для производных.
Ответ №1:
Должны ли мы использовать dynamic_cast или static_cast для вызова Derived::func2?
ДА.
Есть ли другой метод?
Объявите виртуальную Base::func2
функцию.
Чтобы прояснить вопрос о ковариации, это делает это возможным:
Derived d{}; d-gt;getThis()-gt;func2();
Без ковариации Derived* Derived::getThis
это было бы недопустимо, и, следовательно, вышеперечисленное не сработало бы.
Ответ №2:
Ковариантные типы возвращаемых значений-это всего лишь трюк, который позволяет работать принципу подстановки Лискова при незначительном изменении типа возвращаемого значения в интерфейсе производного класса. Но суть LSP остается прежней: интерфейс базового класса-это интерфейс. Это не способ сделать так, чтобы базовый класс прозрачно предоставлял материалы, которые являются исключительно частью производного класса.
Если у вас есть указатель/ссылка на базовый класс, вызов getThis
этого указателя/ссылки всегда будет возвращать a Base*
. Если вы хотите получить некоторые производные элементы только для классов, вы всегда должны использовать какое-то приведение.