Точно, почему 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) не существует.
Потому что неявное преобразование из 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
вместо того, чтобы бросить исключение.
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
Класс А имеет частных / публичных членов.
Класс B является производным от класса A и, возможно, добавил больше частных / открытых членов.
Класс B «является» производным от класса A. Однако класс A «не является» производным от класса B. (То есть: вы можете понижать A-> B, но не повышать B-A.)
Причина в том, что хотя B является разновидностью A, A не является разновидностью B, поэтому методы / члены B не будут присутствовать (даже если метод имеет одинаковое имя в исходном коде, он не будет тем же методом, скомпилированным из-за чтобы имя было скрыто компилятором).