Есть ли неявный конструктор по умолчанию в C ++? - PullRequest
62 голосов
/ 19 февраля 2009

В книге, которую я сейчас читаю ( C ++ Without Fear ), говорится, что если вы не объявите конструктор по умолчанию для класса, компилятор предоставит его вам, что "обнуляет каждый элемент данных ". Я экспериментировал с этим, и я не вижу никакого поведения обнуления. Я также не могу найти ничего, что упоминает об этом в Google. Это просто ошибка или особенность конкретного компилятора?

Ответы [ 11 ]

62 голосов
/ 19 февраля 2009

Если вы не определили конструктор, компилятор определит конструктор по умолчанию для вас.

Реализация этого

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

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

Примечание:
Данные POD (int, float, указатель и т. Д.) Не имеют явного конструктора, но действие по умолчанию - ничего не делать (в соответствии с философией C ++; мы не хотим платить за что-то, если мы явно не просим об этом) .

Если деструктор / копирующий конструктор / оператор присваивания не определен, компилятор создает один из них для вас (поэтому у класса всегда есть деструктор / копирующий конструктор / оператор присваивания (если вы не обманываете и не объявляете его явно, но не определяете его) )).
Реализация по умолчанию:

Destructor:

  • Если определен пользовательский деструктор, выполнить предоставленный код.
  • Вызывать деструктор каждого члена в обратном порядке объявления
  • Позвоните деструктору базового класса.

Конструктор копирования:

  • Вызовите конструктор копирования базового класса.
  • Вызовите конструктор копирования для каждой переменной-члена в порядке объявления.

Оператор присваивания:

  • Вызовите оператор присваивания базового класса
  • Вызвать оператор присваивания каждой переменной-члена в порядке объявления.
  • Вернуть ссылку на это.

Примечание Оператор копирования / назначения копирования данных POD просто копирует данные (отсюда и проблема мелкого копирования, связанная с указателями RAW).

37 голосов
/ 19 февраля 2009

Думаю, стоит указать, что конструктор по умолчанию будет создан компилятором только в том случае, если вы укажете без конструктора . Это означает, что если вы предоставите только один конструктор, который принимает аргумент, компилятор не создаст для вас конструктор по умолчанию без аргументов.

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

34 голосов
/ 13 февраля 2011
  • Компилятор автоматически генерирует конструктор по умолчанию?
  • Неявно ли сгенерированный конструктор по умолчанию выполняет ноль инициализация

Если вы законно анализируете язык стандарта 2003 года, то ответы будут: да и нет . Однако это не вся история , поскольку в отличие от определяемого пользователем конструктора по умолчанию, неявно определенный конструктор по умолчанию не всегда используется при создании объекта с нуля - существует два другие сценарии: без построения и инициализация значения по элементам .

Случай "без конструкции" на самом деле просто техническая, потому что он функционально не отличается от вызова тривиального конструктора по умолчанию. Другой случай более интересен: инициализация значений по элементам вызывается с помощью "()" [как если бы он явно вызывал конструктор без аргументов] и обходит то, что технически называется 1027 * конструктор по умолчанию . Вместо этого он рекурсивно выполняет инициализацию значения для каждого элемента данных, а для примитивных типов данных это в конечном итоге приводит к инициализации нуля .

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

    MyClass a; // default-construction or no construction
    MyClass b = MyClass(); // member-wise value-initialization

и

    new MyClass; // default-construction or no construction
    new MyClass(); // member-wise value-initialization

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


Вот несколько подробное описание того, что стандарт говорит об этом ...

  • Если вы не объявляете конструктор, компилятор неявно создает конструктор по умолчанию [12.1-5]

  • Конструктор по умолчанию не инициализирует типы примитивов [12.1-7]

    MyClass() {} // implicitly defined constructor
    
  • Если вы инициализируете объект с помощью «()», это напрямую не вызывает конструктор по умолчанию. Вместо этого он запускает длинную последовательность правил, которая называется значение-инициализация [8.5-7]

  • Чистый эффект инициализации значения заключается в том, что неявно объявленный конструктор по умолчанию никогда не вызывается . Вместо этого вызывается рекурсивная инициализация значений элементов, которая в конечном итоге инициализирует нулями любые примитивные элементы и вызывает конструктор по умолчанию для любых элементов, которые имеют объявленный пользователем конструктор [8.5-5]

  • Инициализация значений применяется даже к примитивным типам - они будут инициализироваться нулями. [8.5-5]

    a = int(); // equivalent to int a=0;
    

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

Так, когда это имеет значение?

  • Могут быть обстоятельства, когда универсальный код хочет принудительно инициализировать неизвестные типы. Инициализация значения обеспечивает способ сделать это. Просто помните, что неявная инициализация нуля не происходит, если пользователь предоставил конструктор.

  • По умолчанию данные, содержащиеся в std :: vector, инициализируются значением. Это может помешать отладчикам памяти идентифицировать логические ошибки, связанные с неинициализированными буферами памяти.

    vector::resize( size_type sz, T c=T() ); // default c is "value-initialized"
    
  • Целые массивы типа примитивов или структур типа "plain-old-data" (POD) могут быть инициализированы нулями с использованием синтаксиса инициализации значения.

    new int[100]();
    

Этот пост содержит более подробную информацию о различиях между версиями стандарта, а также отмечает случай, когда стандарт применяется по-разному в основных компиляторах.

20 голосов
/ 19 февраля 2009

C ++ генерирует конструктор по умолчанию, но только если вы не предоставляете один из своих. Стандарт ничего не говорит об обнулении членов данных. По умолчанию при первом создании любого объекта он не определен.

Это может сбивать с толку, потому что большинство примитивных типов C ++ имеют «конструкторы» по умолчанию, которые приводят их к нулю (int (), bool (), double (), long (), и т. Д.) ), но компилятор не вызывает их для инициализации членов POD, как это делается для членов объекта.

Стоит отметить, что STL действительно использует эти конструкторы для конструирования по умолчанию содержимого контейнеров, которые содержат примитивные типы. Вы можете взглянуть на этот вопрос для более подробной информации о том, как вещи в контейнерах STL запускаются.

11 голосов
/ 19 февраля 2009

Конструктор по умолчанию, созданный для класса, не будет инициализировать встроенные типы, но вызовет конструктор по умолчанию для всех пользовательских элементов:

class Foo
{
public:
     int x;
     Foo() : x(1) {}
};

class Bar
{
public:
     int y;
     Foo f;
     Foo *fp;
};

int main()
{

    Bar b1; 
    ASSERT(b1.f.x == 1); 
    // We know nothing about what b1.y is set to, or what b1.fp is set to.

    // The class members' initialization parallels normal stack initialization.
    int y;  
    Foo f; 
    Foo *fp; 
    ASSERT(f.x == 1);
    // We know nothing about what y is set to, or what fp is set to.

}
5 голосов
/ 19 февраля 2009

Компилятор сгенерирует конструкторы и деструкторы по умолчанию, если созданные пользователем отсутствуют. Это НЕ изменит состояние каких-либо членов данных.

В C ++ (и C) содержимое любых выделенных данных не гарантируется. В конфигурациях отладки некоторые платформы устанавливают это значение на известное (например, 0xFEFEFEFE), чтобы помочь идентифицировать ошибки, но на это не следует полагаться.

4 голосов
/ 19 февраля 2009

Обнуление происходит только для глобалов. Поэтому, если ваш объект объявлен в глобальной области видимости, его члены будут обнулены:

class Blah
{
public:
    int x;
    int y;
};

Blah global;

int main(int argc, char **argv) {
    Blah local;
    cout<<global.x<<endl;  // will be 0
    cout<<local.x<<endl;   // will be random
}
4 голосов
/ 19 февраля 2009

C ++ не гарантирует обнуление памяти. Java и C # делают (в некотором смысле).

Некоторые компиляторы могут, но не зависят от этого.

2 голосов
/ 13 января 2016

В C ++ 11 конструктор по умолчанию, сгенерированный компилятором, помечается как удаленный, если:

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

http://en.cppreference.com/w/cpp/language/default_constructor

1 голос
/ 30 октября 2013

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

Чтобы конструктор был нетривиальным конструктором, необходимо выполнить следующие условия:

1) В классе есть функция виртуального члена. 2) Подобъекты членов класса или базовые классы имеют нетривиальные конструкторы. 3) У класса есть виртуальная иерархия наследования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...