наследование — почему в C ++ работает приведение к ссылке на производный тип?

Точно, почему B b = (B&) a компилировать и работать тогда как B b = (B) a нет в приведенной ниже программе?

#include <iostream>
using namespace std;

class A {public: void f(){ cout<<"A"<<endl;} };

class B : public A { public: void f(){cout<<"B"<<endl;} };

void g(A a){  B b = (B&) a; b.f(); }

int main() {
B b; g(b);
return 0;
}

Есть ли что-то о приведении к производному типу со ссылкой, которую я здесь пропускаю? Если я просто приведу к B, это даст ошибку времени компиляции, что конструктор B (A a) не существует.

3

Решение

Потому что неявное преобразование из A в B не существует, и вы также не определили явный.

Приведение ссылок, с другой стороны, допустимо, потому что оно разрешено для унаследованных типов. Точнее, вы можете приводить оба пути между разными классами в одной иерархии наследования. То же самое касается указателей. Связанная концепция называется полиморфизм, если вы хотите несколько указателей для дальнейшего изучения.

Заметьте, однако, что это имеет смысл только для объекта, который является типа Bбыть брошенным в B, Е. г .:

B b;
A& aRef = B; // equivalent of A& ref = (A&)B;
B& bRef = (B&)aRef;

То, что вы сделали, потерпит неудачу во время выполнения, как только вы попытаетесь получить доступ к некоторым данным или методу B что не существует в A, Потому что ваш реальный объект Aне B,

Upcasting (от потомка к восходящему) всегда безопасен, потому что любой объект класса, который наследует базовый класс является действительный базовый объект. Унижение, однако, опасно по точной причине, которую я объяснил выше, и никогда не должно быть сделано с использованием броска в стиле C. Вместо этого используйте dynamic_cast:

B b;
A& aRef = B;
B& bRef = dynamic_cast<B&>(aRef);

dynamic_cast использует RTTI (информацию о типе времени выполнения) для проверки операции и выдаст std::bad_cast исключение, если преобразование недействительно. Это непохоже dynamic_castуказатели, в этом случае приведение nullptr вместо того, чтобы бросить исключение.

2

Другие решения

B b = (B) a не будет работать, потому что не определено преобразование (конструктор или оператор преобразования).
B b = (B&) a работает потому что a на ссылку на B (понижен на static_cast), затем вызовите конструктор копирования B. Однако в этом случае a не фактический объект B так что это неопределенное поведение.
Смотрите [expr.static.cast] в стандарте C ++

Если объект типа «cv1 B» на самом деле является подобъектом объекта типа D, результат относится к включающему объекту.
объект типа D. В противном случае поведение не определено.

и [expr.cast] в стандарте C ++ или http://en.cppreference.com/w/cpp/language/explicit_cast а также http://en.cppreference.com/w/cpp/language/cast_operator

1

Класс А имеет частных / публичных членов.
Класс B является производным от класса A и, возможно, добавил больше частных / открытых членов.

Класс B «является» производным от класса A. Однако класс A «не является» производным от класса B. (То есть: вы можете понижать A-> B, но не повышать B-A.)

Причина в том, что хотя B является разновидностью A, A не является разновидностью B, поэтому методы / члены B не будут присутствовать (даже если метод имеет одинаковое имя в исходном коде, он не будет тем же методом, скомпилированным из-за чтобы имя было скрыто компилятором).

-1
По вопросам рекламы [email protected]