Статическое приведение позволяет преобразовывать указатели объектов, но не целые числа - PullRequest
0 голосов
/ 04 июня 2018

Почему статическое приведение допускает повышение или понижение между указателями на производные или базовые объекты, как показано ниже, но в случае преобразования между char * и int * или наоборот int * в char *, возникает ошибка компиляции?

Приведение между указателями на объекты так же плохо, как я считаю.

// compiles fine
class Base {};
class Derived: public Base {};
Base * a = new Base;
Derived * bc = static_cast<Derived*>(a);

// Gives an invalid static cast error during compilation
char charVar = 8;
char* charPtr = &charVar;
int* intPtr = static_cast<int*>(charPtr);

Ответы [ 3 ]

0 голосов
/ 04 июня 2018

Это потому, что вы пытаетесь сделать реинтерпретацию, для которой есть оператор reinterpret_cast<>.static_cast<> для указателей используется только для приведения вниз :

Если new_type является указателем или ссылкой на некоторый класс D, а тип выражения является указателем или ссылкой на его не виртуальную базу B, static_cast выполняет downcast.Это понижение является неправильным, если B является неоднозначным, недоступным или виртуальным основанием (или основанием виртуального основания) для D. Такой static_cast не проверяет во время выполнения, чтобы убедиться, что тип времени выполнения объекта действительно D, и может использоваться толькобезопасно, если это предварительное условие гарантировано другими средствами, например, при реализации статического полиморфизма.Безопасное понижение можно выполнить с помощью dynamic_cast.

См .:

Когда следует использовать static_cast, dynamic_cast, const_cast и reinterpret_cast?

для подробного обсуждения того, когда использовать каждого оператора приведения.

0 голосов
/ 04 июня 2018

C ++ строго ориентирован на производительность.Таким образом, до тех пор, пока есть некоторый сценарий использования, который позволит вам повысить производительность, C ++ позволит вам это сделать.Рассмотрим std::vector: Конечно, есть безопасный доступ к элементу через функцию at, которая выполняет проверку диапазона для вас.Но если вы знаете, что ваши индексы находятся в диапазоне (например, в цикле for), то эти проверки диапазона просто мертвый вес.Таким образом, вы дополнительно получаете (менее безопасный) operator[], который просто пропускает эти проверки.

Аналогично, если у вас есть указатель типа Base, он может в действительности указывать на объект типа Derived.Если вы сомневаетесь, вы бы dynamic_cast с Base* до Derived*.Но это идет с некоторыми накладными расходами.Но если вы точно знаете (каким-либо образом ...), что такое подкласс на самом деле, на 100%, захотите ли вы этого?Поскольку существует естественный (даже неявный!) Путь от Derived* до Base*, мы хотим получить недорогой путь назад.

С другой стороны, между указателями такого естественного приведения нет.совершенно не связанных типов (таких как char и int или два несвязанных класса) и, таким образом, такого недорогого пути назад нет (по сравнению с dynamic_cast, который, конечно же, недоступен).Единственный способ преобразования между ними - это reinterpret_cast.

На самом деле reinterpret_cast также бесплатен, он просто интерпретирует указатель как другой тип со всеми рисками!И reinterpret_cast даже может потерпеть неудачу, если вместо этого потребовался бы static_cast (правильно, чтобы не задавать вопрос «почему бы просто не всегда использовать ...»):

class A { int a; };
class B { };
class C : public A, public B { };

B* b = new C();
C* c = reinterpret_cast<C*>(b); // FAILING!!!

С точки зрения памятимакет, C выглядит так (даже если он скрыт от вас):

class C
{
    A baseA;
    B baseB; // this is what pointer b will point to!
};

Очевидно, мы получим смещение при приведении между C* и B* (в любом направлении), чтосчитается как static_cast и dynamic_cast, но не reinterpret_cast ...

0 голосов
/ 04 июня 2018

Почему статическое приведение позволяет использовать upcast ...

Нет причин предотвращать upcast.На самом деле, производный указатель даже неявно преобразуется в базовый указатель - преобразование не требуется (за исключением сложных случаев, когда существует несколько баз одного типа).Объект производного класса всегда содержит подобъект базового класса.

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

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

Базовый указатель может указывать на базовый подобъект производного объекта в результате восходящего потока.Как, например, здесь:

Derived d;
Base *b = &d;

В некоторых случаях вы можете захотеть получить доступ к членам производного объекта, на которые указывает вы .Статическое приведение делает это возможным без затрат на информацию о типе времени выполнения.

Обычно компилятор не может определить (во время компиляции) конкретный тип указанного объекта (т. Е. Указывает ли указатель)к подобъекту и, если это так, какой тип объекта-контейнера).Программист несет ответственность за соблюдение требований приведенных типов.Если программист не может доказать правильность, то написание статического приведения является ошибкой.

...