Я столкнулся с проблемой, когда приведение к производному классу решило бы проблему. Я нашел ответ на S.O, который говорит, что это может привести к UB, тестированию, он и скомпилирован и работает правильно. Это неопределенное поведение? Если это то, что было бы правильным подходом к этой проблеме?
class A
{
public:
A(){};
~A(){}
};
class B : public A
{
public:
B(){};
~B(){}
void Show() { std::cout << "Show" << std::endl; }
};
int _tmain(int argc, _TCHAR* argv[])
{
A a;
B* b = static_cast<B*>(&a);
b->Show();
return 0;
}
Пока указатель на базовый тип фактически указывает на экземпляр производного типа, такое использование не является неопределенным в соответствии со стандартом C ++. тем не мение, в вашем примере кода указатель b
не указывает на случай B
или любой из его производных типов (которых нет ни одного), он указывает на экземпляр A
, Таким образом, ваш код фактически вызывает неопределенное поведение.
Я нашел ответ на S.O, который говорит, что это может привести к UB, проверяя его,
это скомпилировано и работает правильно.
Тот факт, что некоторый код компилируется и работает правильно, не исключает возможности вызова кода неопределенным поведением, поскольку неопределенное поведение включает в себя «кажется, работает». Причина, по которой вам следует избегать неопределенного поведения, заключается в том, что нет гарантии, что оно будет работать так же, как в следующий раз, когда вы вызовете UB.
Это неопределенное поведение? Если это то, что было бы правильным подходом к
Эта проблема?
В вашем примере да, это неопределенное поведение. Правильный подход будет зависеть от того, что на самом деле должен делать ваш код, поскольку приведенный вами пример является в лучшем случае академическим примером.
Чтобы было понятно, следующая модификация вашего main()
Функция имеет четко определенное поведение и явно разрешена стандартом C ++:
B objectB;
A* ptrA = &objectB;
B* b = static_cast<B*>(ptrA);
b->Show();
Здесь это хорошо определено, потому что указатель ptrA
на самом деле указывает на случай B
даже если сам указатель имеет тип A*
, В приведенном выше примере отливки из A*
в B*
затем вызывает один из B
Функции на приведенном указателе будут работать. Разница в том, что в примере в вашем вопросе, b
на самом деле не указывает на случай B
,
Соответствующий пункт (выделено мной):
Значение типа «указатель на CV1
B
«, гдеB
это тип класса, может быть
преобразован в значение типа «указатель на CV2D
«, гдеD
это класс
выведено (пункт 10) изB
, если действительное стандартное преобразование из
Указатель наD
На указатель наB
» существует (4.10), CV2 та же
резюме-квалификация как или выше резюме-чем квалификация, CV1, а такжеB
является
не виртуальный базовый классD
, Значение нулевого указателя (4.10)
преобразуется в значение нулевого указателя типа назначения. Если
Значение типа «указатель на cv1»B
Указывает наB
это на самом деле
подобъект объекта типаD
результирующий указатель указывает на
ограждающий объект типаD
, В противном случае результат приведения
не определено.
Вы можете привести указатель на базовый объект, который действительно указывает на производный экземпляр, на указатель на производный объект.
Однако в вашем коде объект, на который указывает &a
это не производный объект, а базовый объект, и то, что вы делаете, действительно является неопределенным поведением.
Я знаю, что в реализациях он должен «работать», если у класса нет виртуальных функций или баз, а производный объект не добавляет какой-либо элемент данных, а только методы. Тем не менее это то, что формально не гарантировано работать.
Просто не делай этого.
Приведение разрешено (и, следовательно, работает), если указанный объект действительно является B
, В вашем примере это просто A
и, следовательно, вы получаете неопределенное поведение.
Что поставило вопрос, почему это работает? Это работает (или кажется работать), потому что метод, который вы вызвали, не обращается к объекту. Скорее всего, произойдет сбой, если вы добавите некоторые переменные-члены в B
и попытаться получить к ним доступ в Show()
или если вы делаете Show
виртуальная функция. Но в любом случае это UB, так что в принципе все может случиться.