Хорошо ли задано поведение неинициализированных данных? - PullRequest
6 голосов
/ 26 июля 2011

Примечание: Я использую компилятор g ++ (который, как я слышал, довольно хорош и должен быть довольно близок к стандартному).


У меня есть самый простой класс, о котором я мог подумать:

class BaseClass  {
  public:
    int pub;
};

Затем у меня есть три одинаково простые программы для создания BaseClass объектов и распечатки [неинициализированных] значений их данных.


Дело 1

BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;

Это распечатывает:

B1.pub = 1629556548

Что хорошо. Я действительно думал, что он будет инициализирован до нуля, потому что это POD или Plain Old Datatype или что-то в этом роде, но я не думаю, что? Пока все хорошо.


Дело 2

BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;
BaseClass B2;
cout<<"B2.pub = "<<B2.pub<<endl;

Это распечатывает:

B1.pub = 1629556548
B2.pub = 0

Это определенно странно. Я создал два одинаковых объекта одинаково точно. Один инициализирован, а другой нет.


Дело 3

BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;
BaseClass B2;
cout<<"B2.pub = "<<B2.pub<<endl;
BaseClass* pB3 = new BaseClass;
cout<<"B3.pub = "<<pB3->pub<<endl;

Это распечатывает:

B1.pub = 0
B2.pub = 0
B3.pub = 0

Это самое странное пока. Все они инициализируются до нуля. Все, что я сделал, это добавил две строки кода, и это изменило предыдущее поведение.


Так это просто случай "неинициализированных данных, приводящих к неопределенному поведению" или что-то более логичное происходит "под капотом"?

Я действительно хочу понять поведение конструктора / деструктора по умолчанию, потому что у меня есть ощущение, что это будет очень важно для полного понимания вещей наследования ..

Ответы [ 7 ]

12 голосов
/ 26 июля 2011

Так что это всего лишь случай "неинициализированных данных, приводящих к неопределенному поведению"

Да ...

Иногда, если вы позвоните malloc (или new, который вызывает malloc), вы получите данные, заполненные нулями, потому что они находятся на новой странице ядра. В других случаях он будет полон мусора. Если вы поместите что-то в стек (то есть, в автоматическое хранилище), вы почти наверняка получите мусор - но это может быть сложно отладить, потому что в вашей системе этот мусор может оказаться несколько предсказуемым. А с объектами в стеке вы обнаружите, что изменение кода в совершенно другом исходном файле может изменить значения, которые вы видите в неинициализированной структуре данных.

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

Срок хранения: Существует три вида. Статическая длительность для глобальных переменных, автоматическая для локальных переменных и динамическая для объектов в куче. (Это упрощение и не совсем правильное, но вы можете прочитать стандарт C ++ самостоятельно, если вам нужно что-то совершенно правильное.)

Все, что имеет статическую длительность хранения, инициализируется нулем. Поэтому, если вы создадите глобальный экземпляр BaseClass, то его член pub будет равен нулю (сначала). Поскольку вы помещаете его в стек и кучу, это правило не применяется - и вы ничего не делаете для его инициализации, поэтому он не инициализирован. Он содержит все ненужное, оставленное в памяти последним фрагментом кода для его использования.

Как правило, любой POD в куче или стеке будет неинициализирован, если вы не инициализируете его самостоятельно, и значение будет неопределенным, что может измениться при повторной компиляции или повторном запуске программы. Как правило, любой глобальный POD инициализируется нулем, если вы не инициализируете его чем-то другим.

Обнаружение неинициализированных значений: Попробуйте использовать инструмент memcheck от Valgrind, он поможет вам найти, где вы используете неинициализированные значения - обычно это ошибки.

2 голосов
/ 26 июля 2011

Зависит от того, как вы их объявили:

// Assuming the POD type's
class BaseClass
{
  public:
    int pub;
};

Объекты статической длительности хранения

Эти объекты всегда инициализируются нулями.

// Static storage duration objects:
// PODS are zero initialized.

BaseClass    global; // Zero initialized pub = 0

void plop()
{
    static BaseClass functionStatic;   // Zero initialized.
}

Автоматическая / динамическая продолжительность храненияОбъекты

Эти объекты могут по умолчанию или инициализироваться нулем в зависимости от того, как вы их объявляете.

void plop1()
{
    // Dynamic
    BaseClass*  dynaObj1   = new BaseClass;   // Default initialized (does nothing)
    BaseClass*  dynaObj2   = new BaseClass(); // Zero Initialized

    // Automatic
    BaseClass   autoObj1;                     // Default initialized (does nothing)
    BaseClass   autoObj2   =     BaseClass(); // Zero Initialized

    // Notice that zero initialization of an automatic object is not the same
    // as the zero initialization of a dynamic object this is because of the most
    // vexing parse problem

    BaseClass    autoObj3(); // Unfortunately not a zero initialized object.
                             // Its a forward declaration of a function.
}

Я использую термин «Инициализируемый нулями» / «Инициализация по умолчанию», но технически немного более сложный,«Default-Initialization» становится «без инициализации» элемента pub.В то время как () вызывает 'Value-Initialization', которая становится 'Zero-Initialization' элемента pub.

Примечание. Поскольку BaseClass является POD, этот класс ведет себя так же, как встроенные типы.Если вы меняете BaseClass на любой из стандартных типов, поведение остается тем же.

2 голосов
/ 26 июля 2011

Во всех трех случаях эти объекты POD могут иметь неопределенные значения.

Объекты POD без какого-либо инициализатора НЕ будут инициализироваться значением по умолчанию.Они просто содержат мусор. .

Из стандартных инициализаторов 8.5,

"Если для объекта не указан инициализатор, и объект имеет (возможно, cv-квалифицированный)не относящийся к типу POD (или его массив), объект должен инициализироваться по умолчанию, если объект имеет константный тип, базовый тип класса должен иметь объявленный пользователем конструктор по умолчанию. В противном случае, если инициализатор отсутствуетуказывается для нестатического объекта, объект и его подобъекты, если таковые имеются, имеют неопределенное начальное значение , если объект или любой из его подобъектов имеют тип с константным определением, программа является некорректной. "

Вы можете обнулить все элементы структуры POD следующим образом:

BaseClass object={0};
1 голос
/ 26 июля 2011

Как вы написали свой класс, значение pub не определено и может быть любым. Если вы создадите конструктор по умолчанию, который будет вызывать конструктор по умолчанию pub - он будет гарантированно равен нулю:

class BaseClass  {
  public:
    BaseClass() : pub() {}; // calling pub() guarantees it to be zero.
    int pub;
};

Это было бы гораздо лучше.

0 голосов
/ 26 июля 2011

Дело 1

Независимо от того, является ли инкапсулирующий тип POD, члены-данные встроенного типа не инициализируются по умолчанию инкапсулирующим конструктором по умолчанию.

Дело 2

Нет, ни один не был инициализирован. Базовые байты в позиции памяти одного из них просто оказались , чтобы быть 0.

Дело 3

То же.

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

0 голосов
/ 26 июля 2011

Вот почему вы всегда, всегда, всегда инициализируете классы (или ЛЮБУЮ переменную) в стабильное состояние, а не полагаетесь на то, что компилятор сделает это за вас;тем более что некоторые компиляторы намеренно заполняют их мусором.Фактически, единственный POD MSVC ++, который не заполняется мусором, это bool, они инициализируются как true.Можно было бы подумать, что было бы безопаснее инициировать его в false, но это Microsoft для тебя.

0 голосов
/ 26 июля 2011

Как правило, да, неинициализированные данные приводят к неопределенному поведению.Вот почему другие языки, такие как C #, принимают меры, чтобы вы не использовали неинициализированные данные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...