Два способа вызова конструктора по умолчанию - PullRequest
5 голосов
/ 09 октября 2009

У меня есть следующий код:

struct B
{
 //B() {}
 int x;
 int y;
};

void print(const B &b) 
{
 std::cout<<"x:"<<b.x<<std::endl;
 std::cout<<"y:"<<b.y<<std::endl;
 std::cout<<"--------"<<std::endl;
}

int main()
{
 B b1 = B(); //init1
 B b2; //init2

 print(b1);
 print(b2);

 return 0;
}

Когда я запускаю программу (vs2008, debug), у меня выводится следующее:

x:0
y:0
--------
x:-858993460
y:-858993460
--------

Как видите, b1.x и b1.y имеют значение 0. Зачем? В чем разница между init1 и init2?

Когда я раскомментирую конструктор B, я получаю следующий вывод:

x:-858993460
y:-858993460
--------
x:-858993460
y:-858993460
--------

Может кто-нибудь объяснить причину такого поведения? Tnx заранее.

Ответы [ 5 ]

10 голосов
/ 09 октября 2009

Конструктор по умолчанию для типов POD заполняет его нулями. Когда вы явно определяете свой собственный конструктор, вы не инициализируете x и y и получаете случайные значения (при отладке VS они заполнены точными значениями, но при выпуске они будут случайными).

В соответствии со стандартом C ++ 03 8.5 / 5:

<...> К значение-инициализация объект типа T означает:
- если T является типом класса (раздел 9) с конструктором, объявленным пользователем (12.1), то вызывается конструктор по умолчанию для T (и инициализация является некорректной, если у T нет доступного конструктора по умолчанию);
- если T является типом класса без объединения без конструктора, объявленного пользователем, то каждый нестатический член данных и компонент базового класса в T инициализируются значением;
- если T является типом массива, то каждый элемент инициализируется значением;
- иначе объект инициализируется нулями .

B() - это значение-инициализация временного объекта, которое будет использоваться при копировании-инициализации b1.

В B b2 для объекта не указан инициализатор, поэтому в соответствии с C ++ 03 Standard 8.5 / 9:

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

Чтобы получить нули для b2, вы можете написать B b2 = {};.

5 голосов
/ 09 октября 2009

В обоих случаях этот оператор определяет b1 и копирует-инициализирует его из инициализированного значением временного B объекта.

B b1 = B();

Когда B не имеет объявленного пользователем конструктора, инициализация значения приводит к тому, что вызов членов B будет value-initalized , и для простого типы, такие как int, это означает инициализация нуля .

Когда B имеет объявленный пользователем конструктор, инициализирующее значение пытается вызвать конструктор по умолчанию. Если члены x и y не перечислены в списке инициализатора конструктора, они остаются неинициализированными.

B b2;

В функциях локальные объекты типа POD без инициализатора остаются неинициализированными. Если вы не определяете конструктор для B, это POD-класс, поэтому он применяется, и значения b2.x и b2.y имеют неопределенные значения.

Если объект относится к типу класса, отличному от POD, то он инициализируется по умолчанию , но если это вызывает конструктор, который оставляет его члены неинициализированными, то это не имеет значения.

3 голосов
/ 09 октября 2009

Это инициализация значения, а не инициализация. Если вы напишите

B b1 = B();

Вы получаете инициализацию копирования с временным «инициализированным значением» - B (). Инициализация значений объектов типа класса вызывает определяемый пользователем конструктор (если он существует) или иным образом инициализирует значения членов. Инициализация значений объектов скалярного типа эквивалентна инициализации нуля. Когда вы объявляете свой собственный конструктор, который ничего не делает, ваши скалярные члены не инициализируются.

B b1;

- инициализация по умолчанию или вообще не инициализация (в зависимости от B).

Точные правила инициализации довольно сложны. См. Стандартный раздел C ++ 8.5 «Инициализаторы».

2 голосов
/ 09 октября 2009

О, Кирилл объясняет

Другое значение: -858993460 - 0xCCCCCCCC, которое является значением по умолчанию для неинициализированной стековой памяти в отладочных сборках VC.

Неинициализированная куча памяти по умолчанию 0xCDCDCDCD. И, конечно же, в сборках релизов содержимое по умолчанию является случайным.

Теперь я должен признать, что я не знал, что законно вызывать c'tor напрямую, как вы делаете в своем примере! И я бы сказал, что это незаконно ...

0 голосов
/ 09 октября 2009

я пробовал на vc6 и Visual Studio 2005, я получаю ниже результат в обоих: Не могли бы вы опубликовать код дизассемблирования, как и ниже, я опубликовал код дизассемблирования для 2005

х: -858993460

у: -858993460


х: -858993460

у: -858993460


 B b1 = B(); //init1
0043DEDE  lea         ecx,[b1] 
0043DEE1  call        B::B (43ADD9h) 
 B b2; //init2
0043DEE6  lea         ecx,[b2] 
0043DEE9  call        B::B (43ADD9h) 

 print(b1);
0043DEEE  lea         eax,[b1] 
0043DEF1  push        eax  
0043DEF2  call        print (43A302h) 
0043DEF7  add         esp,4 
 print(b2);
0043DEFA  lea         eax,[b2] 
0043DEFD  push        eax  
0043DEFE  call        print (43A302h) 
0043DF03  add         esp,4 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...