Виртуальной функции 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++ считает его совершенно новой функцией, поэтому вы начинаете перегружаться и скрываться. Дополнительные сведения по этой теме см. В разделе ковариантность и контравариантность (информатика) в Википедии.
реализация производного класса виртуальной функции может иметь Ковариантный Тип Возврата.