#c
#c
Вопрос:
В следующем примере компилятор принимает static_cast
downcast, приводящий к неопределенному поведению, в то время как я думал, static_cast
что все дело в безопасности (что приведения в стиле C не смогли обеспечить).
#include <iostream>
class Base {
public:
int x = 10;
};
class Derived1: public Base
{
public:
int y = 20;
};
class Derived2 : public Base
{
public:
int z = 30;
int w = 40;
};
int main() {
Derived1 d1;
Base* bp1 = static_cast<Base*>(amp;d1);
Derived2* dp1 = static_cast<Derived2*>(bp1);
std::cout << dp1->z << std::endl; // outputs 20
std::cout << dp1->w << std::endl; // outputs random value
}
Комментарии:
1. Я не думаю, что кто-либо когда-либо притворялся, что C — безопасный язык.
2. Нет, для downcasting с безопасностью вам нужны
dynamic_cast
(и проверки null) —static_cast
сообщает компилятору, что вы на 100% уверены, что у вас правильный тип3. Повышающая трансляция выполняется неявно, специального синтаксиса не требуется. Смысл
static_cast
в значительной степени заключается в том, чтобы иметь возможность явно выполнять обратные неявные преобразования. Как правило, невозможно проверить во время компиляции, что такое преобразование допустимо — если бы это было так, компилятор разрешил бы это неявно.4.
static_cast
является «безопасным» в том смысле, что он не позволяет вам конвертировать между несвязанными типами. Цель безопасности C в значительной степени заключается в том, чтобы быть не таким небезопасным, как C.5.
static_cast
обеспечивает всю безопасность, которая может быть обеспечена с нулевыми затратами.dynamic_cast
обеспечивает большую безопасность, но за это приходится платить.
Ответ №1:
Вы используете dynamic_cast
только тогда, когда вы не уверены, что приведение будет успешным, и вы перехватываете исключения или проверяете nullptr. Однако, если вы уверены, что ваша даункастинг будет успешной, язык позволяет вам использовать static_cast
(что дешевле). Если вы ошиблись, это ваша проблема. В идеальном мире каждое приведение было бы успешным в 100% случаев. Но мы не живем в идеальном мире. Это немного похоже на индекс массива. arr[5]
означает «Я абсолютно уверен, что этот массив содержит по крайней мере 6 элементов. Компилятору не нужно проверять «. Если ваш массив был меньше, чем вы ожидали, это снова ваша проблема.
Ответ №2:
Я думал,
static_cast
все дело в безопасности (которую приведение в стиле C не смогло обеспечить)
static_cast
это безопаснее, чем приведение в стиле C. Но не потому, что с этим невозможно ошибиться. Это безопаснее, потому что это только менее вероятно, что что-то пойдет не так. Когда мы пишем приведение в стиле C, компилятор просматривает этот список, чтобы успокоить нас:
Когда встречается выражение приведения в стиле C, компилятор пытается интерпретировать его как следующие выражения приведения, в этом порядке:
const_cast<new_type>(expression)
;static_cast<new_type>(expression)
с расширениями: указатель или ссылку на производный класс дополнительно разрешается приводить к указателю или ссылке на однозначный базовый класс (и наоборот), даже если базовый класс недоступен (то есть это приведение игнорирует спецификатор частного наследования). То же самое относится к приведению указателя на элемент к указателю на элемент однозначной невиртуальной базы;static_cast
(с расширениями), за которым следуетconst_cast
;reinterpret_cast<new_type>(expression)
;reinterpret_cast
за которым следуетconst_cast
.Выбирается первый вариант, который удовлетворяет требованиям соответствующего оператора приведения, даже если он не может быть скомпилирован (см. Пример). Если приведение может быть интерпретировано более чем одним способом, за которым
static_cast
следуетconst_cast
, оно не может быть скомпилировано. Кроме того, в нотации приведения в стиле C разрешено приводить от, к и между указателями на неполный тип класса. Если и expression, и new_type являются указателями на неполные типы классов, не указано, будет ли выбраноstatic_cast
илиreinterpret_cast
.
Смысл в том, чтобы отдавать предпочтение static_cast
этому, заключается в том, что у вас есть более точный контроль над результирующим приведением, что обеспечивает дополнительную безопасность. Но это не меняет того факта, что объектная модель C требует, чтобы static_cast
поддерживалось приведение, даже когда возможно неопределенное поведение. Только dynamic_cast
(кстати, не в приведенном выше списке) имеет дополнительную безопасность для полиморфных типов, но это не без накладных расходов.
Ответ №3:
Я действительно не знаю, что вам сказать. Почему он разрешает такое приведение? Для случаев, когда вам это нужно.
Не хотите его использовать? Не делайте! Вы могли бы переключиться на dynamic_cast
(более дорогой) или не приводить.
C позволяет вам делать множество вещей, требующих обдумывания и осторожности. Это один из них.
Но это все еще безопаснее, чем C. static_cast
Не позволит вам привести, например, bp1
к UrgleBurgle*
.
Конечно, в конечном счете, вы все равно можете использовать приведения в стиле C, если хотите. Я имею в виду, я бы не советовал этого, но вы могли бы. C — это все о выборе (обычно между ужасным вариантом и чуть менее ужасным вариантом).