Является ли неопределенное поведение приведения из базового класса к производному?

Я столкнулся с проблемой, когда приведение к производному классу решило бы проблему. Я нашел ответ на 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;
}

1

Решение

Пока указатель на базовый тип фактически указывает на экземпляр производного типа, такое использование не является неопределенным в соответствии со стандартом 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,


Соответствующий пункт (выделено мной):

Стандарт C ++ 5.2.9 / 8 Статическое приведение [expr.static.cast]

Значение типа «указатель на CV1 B«, где B это тип класса, может быть
преобразован в значение типа «указатель на CV2 D«, где D это класс
выведено (пункт 10) из B, если действительное стандартное преобразование из
Указатель на DНа указатель на B» существует
(4.10), CV2 та же
резюме-квалификация как или выше резюме-чем квалификация, CV1, а также B является
не виртуальный базовый класс D, Значение нулевого указателя (4.10)
преобразуется в значение нулевого указателя типа назначения. Если
Значение типа «указатель на cv1» BУказывает на B это на самом деле
подобъект объекта типа Dрезультирующий указатель указывает на
ограждающий объект типа D, В противном случае результат приведения
не определено.

8

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

Вы можете привести указатель на базовый объект, который действительно указывает на производный экземпляр, на указатель на производный объект.

Однако в вашем коде объект, на который указывает &a это не производный объект, а базовый объект, и то, что вы делаете, действительно является неопределенным поведением.

Я знаю, что в реализациях он должен «работать», если у класса нет виртуальных функций или баз, а производный объект не добавляет какой-либо элемент данных, а только методы. Тем не менее это то, что формально не гарантировано работать.

Просто не делай этого.

2

Приведение разрешено (и, следовательно, работает), если указанный объект действительно является B, В вашем примере это просто A и, следовательно, вы получаете неопределенное поведение.

Что поставило вопрос, почему это работает? Это работает (или кажется работать), потому что метод, который вы вызвали, не обращается к объекту. Скорее всего, произойдет сбой, если вы добавите некоторые переменные-члены в B и попытаться получить к ним доступ в Show() или если вы делаете Show виртуальная функция. Но в любом случае это UB, так что в принципе все может случиться.

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector