@ codekaizer и @Shankar правы: неопределенное поведение по определению не является безопасным поведением типа.Однако, как это относится к примитивным типам, немного сложнее обернуть голову.Представляется разумным, что любая подходящая длинная последовательность битов может быть действительной int
.Как @BoPersson указал ниже, это не совсем верно , и реализации могут включать значения, которые вызывают прерывания в арифметике.Для целых чисел это практически применимо только к 0, когда используется для деления, но это не означает, что стандарт не допускает целочисленную версию чего-то вроде плавающей запятой NaN
для достаточно необычной архитектуры.
Читательможет найти пример с виртуальными функциями, более наглядно показывающий, почему неинициализированные переменные не являются безопасными по типу.Обратите внимание:
struct Base {
virtual int foo() const =0;
};
struct DerivedA : public Base {
int foo() const override { return 10; }
};
struct DerivedB : public Base {
int foo() const override { return -10; }
};
int main() {
Base* abstractStructPtr;
std::cout << abstractStructPtr->foo() << std::endl;
return 0;
}
Тип abstractStructPtr
означает, что вы можете вызвать foo()
на нем.Выражение допустимо: abstractStructPtr
имеет тип, поэтому вы можете вызвать foo()
.Однако реализация foo()
живет в производных классах.
Поскольку abstractStructPtr
не инициализирован, не гарантируется, что данные, на которые он указывает, структурированы таким образом, что он может выполнить вызовдо foo()
.Другими словами, хотя тип absractStructPtr
равен Base*
, нет никакой гарантии, что указанные данные на самом деле являются Base
объектом любого вида.Вызов foo()
, таким образом, является неопределенным поведением и небезопасен.Может произойти все, что угодно;практически он, вероятно, просто рухнет из-за нарушения доступа к памяти, но это не так!Kablooey.