Подтвердить что ты не робот

Конструктор по умолчанию С++

С учетом следующего кода:

class temp
{
public:
    string str;
    int num;
};

int main()
{
    temp temp1;
    temp temp2 = temp();

    cout << temp1.str << endl; //Print ""
    cout << temp2.str << endl; //Print ""

    cout << temp1.num << endl; //Print a rand num
    cout << temp2.num << endl; //Print 0
}

В чем разница между этими двумя? —

temp temp1;

и

temp temp2 = temp();
4b9b3361

Ответ 1

temp temp1;

Это вызывает конструктор по умолчанию temp по умолчанию в экземпляре temp1.

temp temp2 = temp();

Это вызывает конструктор по умолчанию temp по временному объекту, затем вызывает созданный компилятором экземпляр-конструктор на temp2 с временным объектом в качестве аргумента (это, конечно, предполагает, что компилятор не удаляет копии; зависит от ваших настроек оптимизации компилятора).

Что касается того, почему вы получаете разные инициализированные значения, применим раздел 8.5 стандарта:


8.5 Инициализаторы [dcl.init]

Пункт 5:

Для нулевой инициализации объекта типа T означает:
  • если T является скалярным типом (3.9), объект устанавливается в значение 0 (ноль), преобразованное в T;
  • if T - тип неединичного класса, каждый нестатический член данных и каждый подобъект базового класса инициализируются нулем;
  • if T - это тип объединения, объекты, сначала названные членами данных, инициализируются нулем;
  • if T - тип массива, каждый элемент инициализируется нулем;
  • Если T является ссылочным типом, инициализация не выполняется.

Для инициализации по умолчанию объекта типа T означает:

  • Если T - тип класса не-POD (раздел 9), вызывается конструктор по умолчанию для T (и инициализация плохо сформирована, если T не имеет доступного конструктора по умолчанию);
  • if T - тип массива, каждый элемент инициализируется по умолчанию;
  • в противном случае объект инициализируется нулем.

Для инициализации объекта с типом T означает:

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

Пункт 7:

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

Пункт 9:

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

12 Специальные функции членов [специальные]

Пункт 7:

Неявно объявленный конструктор по умолчанию для класса неявно определяется, когда он используется для создания объекта его типа класса (1.8). Неявно определенный конструктор по умолчанию выполняет набор инициализаций класса, который будет выполняться написанным пользователем конструктором по умолчанию для этого класса с пустым списком mem-initializer (12.6.2) и пустым телом функции.

12.6.2 Инициализация баз и членов [class.base.init]

Пункт 4:

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

Итак, теперь, когда правила были изложены, давайте посмотрим, как они применяются:

temp temp1;

temp является не-POD-типом (поскольку он имеет член std::string), и поскольку для temp1 не указан инициализатор, он будет инициализирован по умолчанию (8.5/9). Это вызывает конструктор по умолчанию (8.5/5). temp имеет неявный конструктор по умолчанию (12/7), который по умолчанию инициализирует член std::string, а элемент int вообще не инициализируется (12.6.2/4).

temp temp2 = temp();

С другой стороны, временный объект temp инициализируется значением (8.5/7), значение которого инициализирует все члены данных (8.5/5), который вызывает конструктор по умолчанию в элементе std::string и ноль -инициализирует член int (8.5/5).

Конечно, если вам больше не нужно ссылаться на стандарт в 5+ разных местах, просто убедитесь, что вы явно инициализируете все (например, int i = 0; или используя списки инициализаторов).

Ответ 2

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

Для компиляторов С++ 98 обе декларации одинаково влияют на конечные значения объявляемых объектов: член str должен быть пуст, а члены num должны содержать непредсказуемое значение. В обоих случаях фактическая инициализация - инициализация по умолчанию, выполняемая конструктором по умолчанию класса temp, предоставленным компилятором. Этот конструктор по умолчанию инициализирует str, но оставляет num неинициализированным.

Для компиляторов С++ 03 поведение отличается. Нет никакой разницы для объекта temp1 (его num по-прежнему непредсказуем). Но инициализация temp2 обрабатывается по-разному. В С++ 03 инициализатор () запускает новый тип инициализации - так называемую инициализацию значения. Инициализация значения игнорирует предоставленный компилятором конструктор по умолчанию для объекта верхнего уровня и вместо этого работает непосредственно над его подобъектами (в этом случае членами данных). Таким образом, объект temp2 эффективно инициализируется инициализацией значения, которая также устанавливает член num в ноль (в дополнение к инициализации str с пустой строкой). По этой причине temp2.num заканчивается равным нулю в компиляторах С++ 03.

Если в ваших экспериментах вы наблюдали согласованный ноль в temp2.num, это означает, что ваш компилятор следует спецификации С++ 03 в этом отношении.

Ответ 3

temp temp1;

Создает инициализированный объект temp по умолчанию. Поскольку вы не создали конструктор по умолчанию для temp, каждый элемент temp также будет инициализирован по умолчанию. Поскольку std::string предоставляет значение по умолчанию ctor, он получает правильную инициализацию и имеет четко определенное значение. Целое число, однако, получает инициализацию по умолчанию, которая определяется реализацией и обычно является случайным значением.

temp temp2 = temp();

Сначала создается значение инициализированного объекта temp. Это важно, потому что сам объект инициализируется значением, так же как и его члены. Это не имеет значения для строки, поскольку инициализация по умолчанию и значения одинаковы, но это важно для целого. Целое число, инициализированное значением, имеет значение 0.
После этого вы просто скопируете эти члены в temp2.

Кроме того, этот важный вопрос может вас заинтересовать. Изменить. См. мой комментарий на @In silico для объяснения причин, почему это не относится к MSVC.:/