Я знаю, что инициализация по умолчанию для не-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 ++ пустой конструктор и инициализация члена
Но разница в том, что вместо того, чтобы спрашивать, каково поведение конечного результата, я хотел бы знать, почему происходит поведение конечного результата.
В стандарте 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;
};
Инициализация значения типа с помощью определяемого пользователем конструктора по умолчанию не выполняет инициализацию нестатических элементов 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.
Язык адвокатского аргумента:
T
определяется следующим образом: … Если список инициализаторов не имеет элементов и T
тип класса с конструктором по умолчанию, объект инициализирован значением. «T
означает: … если T
является (возможно, cv-квалифицированным) типом класса (раздел 9) с предоставленным пользователем конструктором (12.1), тогда конструктором по умолчанию для T
называется (и инициализация плохо сформирована, если T
не имеет доступного конструктора по умолчанию)Согласно проекту стандарта 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