Есть ли неявный конструктор по умолчанию в С++?

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

Ответ 1

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

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

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

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

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

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

Destructor:

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

  • Создает ли компилятор конструктор по умолчанию?
  • Создает ли неявно созданный конструктор по умолчанию нулевой инициализации?

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

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

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

    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"
    
  • Целые массивы типов примитивов или структуры типа "простой-старый" (POD) могут быть инициализированы нулем с использованием синтаксиса инициализации значения.

    new int[100]();
    

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

Ответ 4

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

Это может сбивать с толку, потому что большинство примитивных типов С++ DO имеют стандартные "конструкторы", которые инициализируют их до нуля (int(), bool(), double(), long() и т.д.), но компилятор не делает 't вызывайте их для элементов init POD, как это делается для членов объекта.

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

Ответ 5

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

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.

}

Ответ 6

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

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

Ответ 7

С++ не гарантирует обнуление памяти. Java и С# делают (в манере говорить).

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

Ответ 8

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

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
}

Ответ 9

С++ генерирует конструктор по умолчанию. Если необходимо (определяется во время компиляции, я считаю), он также сгенерирует конструктор копии по умолчанию и конструктор назначения по умолчанию. Я ничего не слышал о гарантиях обнуления памяти.

Ответ 10

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

Для того чтобы конструктор был нетривиальным конструктором, следующие условия, в которых может быть достаточно:

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

Ответ 11

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

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

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