Экземпляр класса C ++ не инициализирован, но нет ошибки компиляции, почему - PullRequest
1 голос
/ 22 января 2020

Мой вопрос касается следующего кода:

#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;

class Blob {
public:
    int age;
    void hello() { printf("hello\n"); }
};

void test_case() {
    Blob a;
    //a.hello();         //(1)
    cout << a.age << endl;
}

int main() {
    test_case();
    return 0;
}

, если я закомментирую (1), он скомпилируется успешно. Если раскомментировать out (1), возникает ошибка компиляции, например, в VS2017 он жалуется на «использование не инициализированной локальной переменной« a »».

Я некоторое время искал в поисковой системе и теперь знаю только следующие 4 случаи, когда компилятор автоматически помогает мне определить конструктор по умолчанию:

  1. член класса является экземпляром класса (скажем, B), а класс B определил конструктор по умолчанию

  2. класс является производным от другого класса (скажем, B), и B определил конструктор по умолчанию

  3. класс имеет виртуальную функцию

  4. любая комбинация предыдущих 3 случаев.

Мне любопытно, что, если я закомментирую (1), компилятор добавит определение конструктора для класса Blob

Ответы [ 2 ]

3 голосов
/ 22 января 2020

На самом деле это не имеет никакого отношения к комментарию или нет a.hello();, Blob всегда имеет сгенерированный конструктор по умолчанию , иначе Blob a; не скомпилируется.

(выделено мной)

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

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

В результате a.age равен по умолчанию инициализируется для неопределенного значения, любой доступ к нему (например, cout << a.age;) приводит к UB.

3), когда базовый класс или элемент данных не в состоянии c не упоминается в списке инициализатора конструктора и этот конструктор вызывается.

в противном случае ничего не делается: объекты с автоматическими c продолжительностью хранения (и их подобъектами) инициализируются для неопределенного значения val ues .

Это зависит от ваших намерений; В качестве обходного пути вы можете добавить пользовательский конструктор по умолчанию.

class Blob {
public:
    int age;
    void hello() { printf("hello\n"); }
    Blob() : age(42) {}
};
1 голос
/ 22 января 2020

Здесь есть 2 пункта.

Первый вопрос - неопределенное поведение. Так как вы не инициализируете age, он содержит неопределенное значение, которое приводит к UB, если вы его используете (см. Ответы songyuanyao для получения более подробной информации об этом). Добавление дополнительной инструкции к этому моменту ничего не меняет.

Далее идет сообщение компилятора. Компиляторы не обязаны выдавать какие-либо предупреждения перед UB. Ваш не является особенно последовательным, если он вызывает ошибку только в одном случае, но программист должен никогда не писать UB. Таким образом, вы не можете винить компилятор в том, что он не выдал предупреждение.

TL / DR: не ожидайте, что компилятор C ++ всегда выдаст предупреждение, когда вы пишете неправильный код. Отсутствие предупреждения является необходимым условием, но не достаточным.

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