Запутался, как пустой пользовательский конструктор будет инициализировать нестатические переменные, не являющиеся членами POD

Я знаю, что инициализация по умолчанию для не-POD-типов также будет инициализировать нестатические переменные-члены-не-POD по умолчанию, вызывая их конструктор по умолчанию. Но я не уверен, как именно это происходит. Вот пример того, что я имею в виду:

#include <iostream>
#include <vector>
using namespace std;

class Test2 {
public:
Test2() {cout <<"Here";}
};

class Test {
public:
Test() {}
Test2 i;
};

int main() {
Test foo;
}

Выход:

Here

На основе стандарта C ++ для инициализаторов (8.5), для инициализации по умолчанию:

— if T is a non-POD class type (clause 9), the default constructor
for T is called (and the initialization is ill-formed if T has no
accessible default constructor);

Поэтому, учитывая это, я ожидаю, что конструктор по умолчанию Test() будет вызван, но мой пустой конструктор по умолчанию для класса Test не инициализируется Test2 i явно, но ясно, Test2() вызывается как-то неявно. Что мне интересно, как это происходит?

Точно так же, для инициализации значения (не связанной с примером выше), если пустой определяемый пользователем конструктор по умолчанию явно не обнуляет инициализацию нестатической переменной-члена POD, как эта переменная инициализируется нулем (что, я знаю, делает)? Поскольку, основываясь на стандарте, кажется, что для инициализации значения все, что происходит, когда у вас есть определяемый пользователем конструктор по умолчанию, это то, что конструктор вызывается.

Соответствующая часть стандарта C ++ для инициализации значения выглядит следующим образом:

— if T is a class type (clause 9) with a user-declared constructor (12.1), then the
default constructor for T is called (and the initialization is ill-formed if T has no
accessible default constructor);

Этот вопрос похож на c ++ пустой конструктор и инициализация члена
Но разница в том, что вместо того, чтобы спрашивать, каково поведение конечного результата, я хотел бы знать, почему происходит поведение конечного результата.

2

Решение

В стандарте C ++ 11, раздел 12.6, пункт 8:

В не делегирующем конструкторе, если заданный нестатический элемент данных или базовый класс не обозначен mem-initializer-id (включая случай, когда нет mem-initializer-list, потому что конструктор не имеет ctor-initializer) и сущность не является виртуальным базовым классом абстрактного класса (10.4), то

  • если объект является нестатическим элементом данных, который имеет инициализатор с фигурной скобкой или равным значением, объект инициализируется
    как указано в 8.5;
  • в противном случае, если объект является вариантом члена (9.5), инициализация не выполняется;
  • в противном случае объект инициализируется по умолчанию (8.5).

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

Также из пункта 10:

В не делегирующем конструкторе инициализация происходит в следующем порядке:
— Во-первых, и только для конструктора самого производного класса (1.8), виртуальные базовые классы инициализируются в том порядке, в котором они отображаются при обходе слева направо по глубине направленного ациклического графа базовых классов, где «слева» -to-right »- порядок появления базовых классов в списке базовых спецификаторов производного класса.

  • Затем прямые базовые классы инициализируются в порядке объявления, как они появляются в списке базовых спецификаторов.
    (независимо от порядка установки mem-инициализаторов).
  • Затем не статические члены данных инициализируются в порядке, в котором они были объявлены в определении класса.
    (опять же независимо от порядка mem-инициализаторов).
  • Наконец, составной оператор тела конструктора выполняется.

Независимо от того, что вы указали в своем конструкторе, члены будут инициализированы непосредственно перед выполнением тела конструктора.

Mem-initializer-id — это идентификатор, используемый для ссылки на член в списке инициализатора конструктора:

class Test {
public:
Test() : i() {} // Here `i` is a mem-initializer-id
Test2 i;
};
2

Другие решения

Инициализация значения типа с помощью определяемого пользователем конструктора по умолчанию не выполняет инициализацию нестатических элементов POD, если они не были явно инициализированы в конструкторе. Например, в эта программа:

#include <iostream>

using namespace std;

struct Foo {
// Foo has a user-defined default constructor
// that does not initialize i
Foo() {}
int i;
};

int main() {
Foo x{}; // value-initialize x
cout << x.i << endl;
}

x.i неинициализирован. Следовательно, технически программа имеет неопределенное поведение, но в этом случае «неопределенное поведение», скорее всего, означает, что она выведет неопределенное целочисленное значение, которое, вероятно, не равно 0.

Язык адвокатского аргумента:

  • §12.6.1p2: «Объект типа класса также может быть инициализирован приготовился-INIT-лист. Применяется семантика инициализации списка; см. 8.5 и 8.5.4. «
  • §8.5.4p3: «Инициализация списка объекта или ссылки типа T определяется следующим образом: … Если список инициализаторов не имеет элементов и T тип класса с конструктором по умолчанию, объект инициализирован значением. «
  • §8.5p7: «Кому Значение инициализации объект типа T означает: … если T является (возможно, cv-квалифицированным) типом класса (раздел 9) с предоставленным пользователем конструктором (12.1), тогда конструктором по умолчанию для T называется (и инициализация плохо сформирована, если T не имеет доступного конструктора по умолчанию)
2

Согласно проекту стандарта C ++, найденному на http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf, раздел 12.6.2:

Если данный нестатический элемент данных или базовый класс не назван с помощью mem-initializer-id (включая случай, когда нет mem-initializer-list, потому что конструктор не имеет ctor-initializer), тогда

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

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

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

Также обратите внимание, что другие типы (например, примитивы и типы POD) не инициализируются, что отличается от того, что вы указали в своем вопросе. Глобальные объекты инициализируются нулями, но это не относится к объектам в стеке. Вот небольшая программа, которую я собрал, чтобы проверить это:

#include <iostream>

class T
{
public:
T() {}

void put(std::ostream &out)
{
out << "a = " << a << std::endl;
out << "b = " << b << std::endl;
out << "c = " << c << std::endl;
}
private:
int a;
int b;
int c;
};

T t2;

int main()
{
T t;
t.put(std::cout);
t2.put(std::cout);

return 0;
}

Компилируя с g ++ 4.5.2, я получил следующий вывод:

a = 8601256
b = 3
c = 2130567168
a = 0
b = 0
c = 0
0
По вопросам рекламы [email protected]