reinterpret_cast и кросс-кастинг

#include<iostream>

struct I1
{
virtual void nb1()=0;
virtual ~I1(){}
};

struct I2
{
virtual void nb2()=0;
virtual void nb22()=0;
virtual ~I2(){}
};

struct C12 : I1, I2
{
virtual void nb1(){std::cout << "nb\n";}
virtual void nb2(){std::cout << "nb2\n";}
virtual void nb22(){std::cout << "nb22\n";}
};

int main()
{
I2 * p2 = new C12;
I1 * p1 = reinterpret_cast<I1*>(p2);
return 1;
}

Есть ли противопоказания к применению reinterpret_cast Вот ? Должен ли я обязательно использовать dynamic_cast?

Это нормально, только если I1 и I2 чисто виртуальные?

1

Решение

reinterpret_cast небезопасно; вы должны всегда использовать dynamic_cast, Не имеет значения, что I1 а также I2 являются чисто виртуальными классами; как подобъекты C12 они имеют ненулевые требования к хранению. Действительно, в следующей программе:

int main() {
I2 * p2 = new C12;
I1 * p1 = reinterpret_cast<I1*>(p2);
std::cout << p2 << '\n';
std::cout << p1 << '\n';
std::cout << dynamic_cast<I1*>(p2) << '\n';
p1->nb1();
}

выход:

0x14de0c8
0x14de0c8
0x14de0c0   // !!!
nb2         // !!!

Вы можете думать о оптимизация пустой базы (1.8p5); но это не относится к классам с виртуальными методами, которые требуют vptr.

3

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

Есть ли противопоказания к применению reinterpret_cast Вот ?

Не делай этого; это даст неопределенное поведение.

Как правило, базовые подобъекты хранятся в разных местах в пределах всего объекта, так что приведение будет в конечном итоге указывать на неправильное местоположение. Вы можете обнаружить, что он «работает» случайно с этими пустыми базовыми классами, поскольку они могут (или не могут) оказаться в одном месте; но вы, конечно, не можете полагаться на такое поведение.

Должен ли я обязательно использовать dynamic_cast?

Если вы не знаете общий производный тип (C12) во время компиляции, тогда это единственный разумный вариант. Обратите внимание, что это требует полиморфных классов (что имеет место здесь).

Если вы знаете общий производный тип, вы можете конвертировать через него, используя static_cast:

I1 * p1 = static_cast<C12*>(p2);

Обратите внимание, что «upcast» от производного класса может быть сделан неявно; только «downcast» из базового класса нуждается в явном приведении.

3

Поскольку компилятор может быть зарезервируйте несколько байтов для каждого из двух подобъектов C12, даже если у них нет членов или базовых классов, и с тех пор адреса этих двух подобъектов различны, это может привести к проблемам. Стандарт просто говорит, что результат этого приведения не определен (§5.2.10,7).

1

Это почти всегда плохая идея использовать reinterpret_cast,

Здесь вы можете сделать либо dynamic_cast или двойной static_cast (через базовый класс).


С точки зрения ABI ваш C12 является:

struct C12 {
I1 parent1;
I2 parent2;
};

C12 obj;

Теперь у вас есть указатель на внутренний obj.parent2, который не obj,

0

Если у вас есть объект x типа C12 и три указателя на него, как в:

C12 x;
C12 *pA = &x;
I1  *pB = &x;
I2  *pC = &x;

тогда указатели pA, pB и pC не обязательно равны. Таким образом, вы не можете просто переосмыслить трансляцию назад и вперед между pA, pB и pC. Вы можете безопасно static_cast или dynamic_cast между pA и pB и между pA и pC. Таким образом, чтобы получить I1 * от I2 *, вам нужно привести ваш p2 сначала к C12 *, затем к I1 *. Выберите строку, которая вам нравится больше всего:

I1 * p1 = static_cast<C12*>(p2);
I1 * p1 = dynamic_cast<C12*>(p2);
0
По вопросам рекламы [email protected]