Как члены класса C ++ инициализируются, если я не делаю это явно? - PullRequest
127 голосов
/ 27 июня 2010

Предположим, у меня есть класс с личными членами ptr, name, pname, rname, crname и age.Что произойдет, если я сам их не инициализирую?Вот пример:

class Example {
    private:
        int *ptr;
        string name;
        string *pname;
        string &rname;
        const string &crname;
        int age;

    public:
        Example() {}
};

А потом я делаю:

int main() {
    Example ex;
}

Как члены инициализируются в ex?Что происходит с указателями?Получают ли string и int инициализацию 0 с конструкторами по умолчанию string() и int()?Как насчет референтного члена?А как насчет константных ссылок?

Что еще я должен знать?

Кто-нибудь знает учебник, который охватывает эти случаи?Может быть, в некоторых книгах?У меня есть доступ в университетской библиотеке ко многим книгам по С ++.

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

Ответы [ 7 ]

167 голосов
/ 27 июня 2010

Вместо явной инициализации инициализация членов в классах работает идентично инициализации локальных переменных в функциях.

Для объектов вызывается их конструктор по умолчанию.Например, для std::string конструктор по умолчанию устанавливает его в пустую строку.Если класс объекта не имеет конструктора по умолчанию, это будет ошибкой компиляции, если вы явно не инициализируете его.

Для примитивных типов (указатели, целые и т. Д.) Они являются not initialized - они содержат любой произвольный мусор, который раньше находился в этом месте памяти.

Для ссылок (например, std::string&), это незаконно не инициализировать их, и ваш компилятор будет жаловаться и откажется компилировать такой код.Ссылки всегда должны быть инициализированы.

Итак, в вашем конкретном случае, если они не инициализированы явно:

    int *ptr;  // Contains junk
    string name;  // Empty string
    string *pname;  // Contains junk
    string &rname;  // Compile error
    const string &crname;  // Compile error
    int age;  // Contains junk
26 голосов
/ 27 июня 2010

Во-первых, позвольте мне объяснить, что такое mem-initializer-list . mem-initializer-list - это разделенный запятыми список mem-initializer s, где каждый mem-initializer является именем члена, за которым следует (за которым следует выражение-список , за которым следует ). выражение-список - это то, как создается член.Например, в

static const char s_str[] = "bodacydo";
class Example
{
private:
    int *ptr;
    string name;
    string *pname;
    string &rname;
    const string &crname;
    int age;

public:
    Example()
        : name(s_str, s_str + 8), rname(name), crname(name), age(-4)
    {
    }
};

mem-initializer-list предоставленного пользователем конструктора без аргументов равен name(s_str, s_str + 8), rname(name), crname(name), age(-4).Этот mem-initializer-list означает, что элемент name инициализируется конструктором std::string, который принимает два входных итератора , элемент rname инициализируется со ссылкой наname, элемент crname инициализируется с помощью const-ссылки на name, а элемент age инициализируется значением -4.

Каждый конструктор имеет свой собственный mem-initializer-list , и члены могут быть инициализированы только в предписанном порядке (в основном порядок, в котором члены объявлены в классе).Таким образом, элементы Example могут быть инициализированы только в следующем порядке: ptr, name, pname, rname, crname и age.

Когда вы это делаетене указывайте mem-инициализатор члена, стандарт C ++ гласит:

Если объект является нестатическим членом данных ... типа класса ..., объектинициализируется по умолчанию (8.5).... В противном случае сущность не инициализируется.

Здесь, поскольку name является нестатическим членом данных типа класса, он инициализируется по умолчанию, если инициализатор для name не был указан в mem-initializer-list .Все остальные члены Example не имеют типа класса, поэтому они не инициализируются.

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

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

9 голосов
/ 29 октября 2014

Вы также можете инициализировать элементы данных в той точке, в которой вы их объявили:

class another_example{
public:
    another_example();
    ~another_example();
private:
    int m_iInteger=10;
    double m_dDouble=10.765;
};

Я использую эту форму в основном исключительно, хотя я читал, что некоторые люди считают ее «плохой формой», возможно, потому, что она былатолько недавно представленный - я думаю, в C ++ 11.Для меня это более логично.

Еще один полезный аспект новых правил - как инициализировать данные-члены, которые сами являются классами.Например, предположим, что CDynamicString является классом, который инкапсулирует обработку строк.Он имеет конструктор, который позволяет указать его начальное значение CDynamicString(wchat_t* pstrInitialString).Вы могли бы очень хорошо использовать этот класс в качестве члена данных внутри другого класса - скажем, класса, который инкапсулирует значение реестра Windows, которое в этом случае хранит почтовый адрес.Чтобы «жестко запрограммировать» имя раздела реестра, в которое записывается этот код, вы используете фигурные скобки:

class Registry_Entry{
public:
    Registry_Entry();
    ~Registry_Entry();
    Commit();//Writes data to registry.
    Retrieve();//Reads data from registry;
private:
    CDynamicString m_cKeyName{L"Postal Address"};
    CDynamicString m_cAddress;
};

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

7 голосов
/ 27 июня 2010

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

Для глобального экземпляра неинициализированные скалярные члены будут обнулены.

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

  • int *ptr; // неинициализированный указатель (или обнуляется, если глобальный)
  • string name; // вызван конструктор, инициализирован пустой строкой
  • string *pname; // неинициализированный указатель (или обнуляется, если он глобальный)
  • string &rname; // ошибка компиляции, если вам не удалось инициализировать
  • const string &crname; // ошибка компиляции, если не удалось инициализировать
  • int age; // скалярное значение, неинициализированное и случайное (или обнуляется, если оно глобально)
4 голосов
/ 27 июня 2010

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

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

В вашем примере:

int *ptr; // will point to a random memory location
string name; // empty string (due to string's default costructor)
string *pname; // will point to a random memory location
string &rname; // it would't compile
const string &crname; // it would't compile
int age; // random value
1 голос
/ 27 июня 2010

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

Вы не можете зависеть от содержимого других типов.

0 голосов
/ 27 июня 2010

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

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

...