Как сделать так, чтобы определяемый пользователем пустой конструктор по умолчанию вел себя так, как определяемый компилятором пустой конструктор - PullRequest
8 голосов
/ 10 апреля 2019

Я пытаюсь выучить C ++ около месяца, и это все еще озадачивает меня. Например, этот «простой» код:

class A
{
    int m;
public:
    A() = default;
    int getM() { return m; }
};

class B
{
    int m;
public:
    // empty body and empty initializer list
    B() {} // isn't this the same as "B() = default;" ?
    int getM() { return m; }

};

int main()
{
    A a1;
    A a2{};

    std::cout << "a1.m=" << a1.getM() << "\n";
    std::cout << "a2.m=" << a2.getM() << "\n";

    B b1;
    B b2{};

    std::cout << "b1.m=" << b1.getM() << "\n";
    std::cout << "b2.m=" << b2.getM() << "\n";

    std::cin.ignore();
}

результат:

a1.m=...garbage
a2.m=0
b1.m=...garbage
b2.m=...garbage

Согласно CPP REFERENCE конструктор по умолчанию, определенный компилятором, имеет пустое тело и пустой список инициализатора. Так как, черт возьми, это (в классе A) инициализировать член 'm' в ноль, если явно не задан конструктор по умолчанию с пустым телом и пустым списком инициализатора? Согласно отрывку cppreference:

If the implicitly-declared default constructor is not defined as deleted,  it is defined (that is, a function body is generated and compiled)
by the compiler if odr-used, and it has exactly the same effect as a user-defined constructor with empty body and empty initializer list. 

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

Ответы [ 2 ]

2 голосов
/ 10 апреля 2019

Если предусмотрен какой-либо конструктор, инициализация нуля не происходит, как указано здесь

Инициализация нуля выполняется в следующих ситуациях:

...

2) Как часть последовательности инициализации значения для типов, не относящихся к классам, и для членов типов классов, инициализированных значением, , которые не имеют конструкторов , включая инициализацию значений элементов агрегатов, для которыхинициализаторы не предоставляются.

Также здесь

Эффект инициализации значения:

1) , еслиT является типом класса по крайней мере с одним предоставленным пользователем конструктором любого вида, конструктор по умолчанию называется;

Что имеет смысл.Нужно уметь контролировать, можно ли добавлять какие-либо дополнительные операции.Если вы предоставляете конструктор, вы берете на себя ответственность за инициализацию вашего объекта.

2 голосов
/ 10 апреля 2019

Вот основная идея.И A a2{};, и B b2{}; будут выполнять то, что называется " инициализация значения " для двух объектов.Однако способ инициализации значения зависит от того, как определены эти типы.

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

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

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

Почему правила говорят это?Потому что = default - это , который не должен быть эквивалентным пустому телу конструктора.В самом деле, отличия - вот почему = default существует как элемент.

Когда вы = default используете конструктор по умолчанию, вы говорите «сгенерируйте конструктор по умолчанию, как обычно».Это важно, потому что есть вещи, которые вы можете сделать, которые активно мешают компилятору генерировать конструктор по умолчанию для вас.Если вы укажете другие конструкторы (которые не являются конструкторами копирования / перемещения), компилятор не сгенерирует их автоматически.Таким образом, используя синтаксис = default, вы сообщаете компилятору, что вам нужен сгенерированный конструктор по умолчанию.

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

Если бы = default и пустое тело вели себя одинаково, у вас не было бы способа получить такое поведение, сказать, что вы хотите инициализацию ваших членов по умолчанию независимо от того, что.

По сути, утверждение Cppreference совершенно неверно;он не имеет «точно такого же эффекта, как пользовательский конструктор с пустым телом и пустым списком инициализатора».И не предполагается.


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

int i{};

Это гарантированно даст значение 0для i.Поэтому разумно, что это:

struct S{int i;};
S s{};

Если также даст значение 0 для s.i.Как это происходит?Поскольку инициализация значения будет инициализироваться нулями s.

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

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