Написание конструктора по умолчанию вызывает нулевую инициализацию?

Это мои определения классов:

class Foo{
    int _ent;
public:
    void printEnt() const{cout << _ent << ' ';}
};

class Bar{
    Foo _foo;
public:
    void printEnt() const{_foo.printEnt();}
};

И это мой тестовый код:

char* buf = new char[sizeof(Foo) + sizeof(Foo) + sizeof(Bar)];

fill(buf, buf + sizeof(Foo) + sizeof(Foo) + sizeof(Bar), 'J');

cout << ((int*)buf)[0] << ' ' << ((int*)buf)[1] << ' ' << ((int*)buf)[2] << endl;

Foo* first = new (buf) Foo;
Foo* second = new (buf + sizeof(Foo)) Foo();
Bar* third = new (buf + sizeof(Foo) * 2) Bar;

first->printEnt(); second->printEnt(); third->printEnt();

Мой вывод:

1246382666 1246382666 1246382666
1246382666 0 1246382666

Но если я добавлю public ctor по умолчанию для Foo: Foo() : _ent(0) {}

Мой вывод становится:

1246382666 1246382666 1246382666
0 0 0

Это правильное поведение? Должен ли мой собственный ctor по умолчанию удалить возможность инициализации по умолчанию?

Я запускаю этот код на GCC 4.8.1, если это имеет значение. Результаты должны быть надежными, потому что я работаю в режиме отладки и утверждаю: assert(sizeof(Foo) == sizeof(int) && sizeof(Bar) == sizeof(int));

3 ответа

Решение

Как только вы предоставите конструктор для типа, он будет всегда вызываться как для инициализации по умолчанию, так и для инициализации значения. Это фундаментальный принцип языка. Так что, как только вы определите Foo::Foo()будет вызываться каждый раз, когда вы создаете Foo; если есть конструктор по умолчанию, он будет вызван даже в случае инициализации по умолчанию. Таким образом, поведение, которое вы видите, является правильным.

РЕДАКТИРОВАТЬ:

Инициализация по умолчанию объясняется §8.5/7, в частности:

По умолчанию инициализировать объект типа T означает:

- если T является (возможно, cv-квалифицированным) типом класса (раздел 9), конструктор по умолчанию (12.1) для T называется [...]

В вашем случае вы, вероятно, также захотите посмотреть, как компилятор генерирует конструктор по умолчанию, если ничего не указано, §12.1/4; в частности, сгенерированный конструктор по умолчанию вызывает конструктор по умолчанию любых базовых классов или членов.

Значение инициализации находится в §8.5/8. Это по умолчанию инициализация по умолчанию, которой предшествует нулевая инициализация, так что инициализация по умолчанию, которая ничего не делает, все равно находит все инициализированные нулями.

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

На ваш вопрос ответили в C++11 версии стандарта:

class Foo{
    int _ent=0;
public:

    // ...
};

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

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

Другие вопросы по тегам