Виртуальной функции C++ тип возвращаемого значения
возможно ли для унаследованного класса реализовать виртуальную функцию с другим типом возврата (не используя шаблон в качестве возврата)?
3 ответа:
в некоторых случаях, да, это законно для производного класса, чтобы переопределить виртуальную функцию, используя другой тип возвращаемого значения, пока тип возвращаемого значения ковариантные С исходным типом возврата. Например, рассмотрим следующее:
class Base { public: virtual ~Base() {} virtual Base* clone() const = 0; }; class Derived: public Base { public: virtual Derived* clone() const { return new Derived(*this); } };здесь
Baseопределяет чисто виртуальную функцию с именемcloneчто возвращаетBase *. В производной реализации эта виртуальная функция переопределяется с помощью возвращаемого типаDerived *. Хотя тип возвращаемого значения не то же самое, что в базе, это совершенно безопасно, потому что в любое время вы будете писатьBase* ptr = /* ... */ Base* clone = ptr->clone();вызов
clone()всегда будет возвращать указатель наBaseобъект, так как даже если он возвращаетDerived*, этот указатель неявно преобразуется вBase*и операция четко определена.как правило, возвращаемый тип функции никогда не считается частью ее сигнатуры. Можно переопределить функцию-член с любым типом возвращаемого значения, если тип возвращаемого значения является ковариантным.
да. Возвращаемые типы могут быть разными, если они ковариантные. Стандарт C++ описывает его следующим образом (§10.3/5):
тип возврата переопределяющей функции должен быть либо идентичен типу возврата переопределенной функции, либо ковариантные С классами функций. Если функция
D::fпереопределяет функциюB::f, возвращаемый тип функций ковариантен, если удовлетворяют следующим критериям:
- оба являются указателями на классы или ссылки на классы98)
- класс в возвращаемом типе
B::fэто тот же класс, что и класс в возвращаемом типеD::fили, является однозначным прямым или косвенным базовым классом класса в возвращаемом типеD::fи работает вD- оба указателя или ссылки имеют одинаковую cv-квалификацию и тип класса в возвращаемый тип
D::fимеет ту же CV-квалификацию, что и CV-квалификация, или меньше, чем тип класса в возвращаемом типеB::f.сноска 98 указывает, что " многоуровневые указатели на классы или ссылки на многоуровневые указатели на классы не допускаются."
короче, если
Dявляется подтипомB, то возвращаемый тип функции вDдолжен быть подтип возвращаемого типа функции вB. Этот наиболее распространенный пример - это когда возвращаемые типы сами основаны наDиB, но они не должны быть. Рассмотрим это, где мы две отдельные иерархии типов:struct Base { /* ... */ }; struct Derived: public Base { /* ... */ }; struct B { virtual Base* func() { return new Base; } virtual ~B() { } }; struct D: public B { Derived* func() { return new Derived; } }; int main() { B* b = new D; Base* base = b->func(); delete base; delete b; }причина, по которой это работает, заключается в том, что любой вызывающий
funcждетBaseуказатель. ЛюбойBaseуказатель будет делать. Так что, еслиD::funcобещает всегда возвращать aDerivedуказатель, то он всегда будет удовлетворять контракту, выложенному классом предка, потому что любойDerivedуказатель может быть неявно преобразуется вBaseуказатель. Таким образом, абоненты всегда будут получать то, что они ожидают.
в дополнение к разрешению типа возврата варьироваться, некоторые языки позволяют типы параметров основной функцией отличаться. Когда они это делают, они обычно должны быть контравариантным. То есть, если
B::fпринимает aDerived*, потомD::fбудет разрешено принятьBase*. Потомкам разрешено быть слабее в том, что они примут, и жесткие в чем они возвращаются. C++ не допускает контравариантность типа параметра. Если вы измените типы параметров, C++ считает его совершенно новой функцией, поэтому вы начинаете перегружаться и скрываться. Дополнительные сведения по этой теме см. В разделе ковариантность и контравариантность (информатика) в Википедии.
реализация производного класса виртуальной функции может иметь Ковариантный Тип Возврата.