Предположим , у меня есть класс с частными ЧЛЕНОВ 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-Instalized с конструкторами по умолчанию string()
и int()
? Как насчет референтного члена? А как насчет постоянных ссылок?
О чем еще я должен знать?
Кто-нибудь знает учебник, который охватывает эти случаи? Может быть, в некоторых книгах? У меня есть доступ в университетской библиотеке ко многим книгам по С ++.
Я хотел бы изучить это, чтобы я мог писать лучше (без ошибок) программ. Любой отзыв поможет!
c++
initialization
member-initialization
bodacydo
источник
источник
Ответы:
Вместо явной инициализации инициализация членов в классах работает так же, как и инициализация локальных переменных в функциях.
Для объектов вызывается их конструктор по умолчанию. Например, для
std::string
конструктора по умолчанию устанавливается пустая строка. Если класс объекта не имеет конструктора по умолчанию, это будет ошибкой компиляции, если вы явно не инициализируете его.Для примитивных типов (указатели, целые и т. Д.) Они не инициализируются - они содержат любой произвольный мусор, который раньше находился в этом месте памяти.
Что касается ссылок (например
std::string&
), запрещается их не инициализировать, и ваш компилятор будет жаловаться и отказываться компилировать такой код. Ссылки всегда должны быть инициализированы.Итак, в вашем конкретном случае, если они не инициализированы явно:
источник
foo
него есть конструктор, он просто неявный. Но это действительно аргумент семантики.Сначала позвольте мне объяснить, что такое mem-initializer-list . Список MEM-инициализатор- это разделенный запятыми список MEM-инициализатор с, где каждый MEM-инициализатор представляет собой имя элемента с последующим
(
, с последующим выражением-лист , за которым следует)
. Список выражений - это то, как создается член. Например, вает-инициализатор-лист конструктора предоставленного пользователя, не-аргументы
name(s_str, s_str + 8), rname(name), crname(name), age(-4)
. Этот MEM-инициализатор-списка означает , чтоname
элемент инициализируется вstd::string
конструкторе , который принимает два входных итераторов , тоrname
элемент инициализируется со ссылкойname
, тоcrname
элемент инициализируется с константной-ссылкойname
, аage
элемент инициализируются со значением-4
.Каждый конструктор имеет свой собственный список mem-initializer-list , и члены могут быть инициализированы только в установленном порядке (в основном, в порядке, в котором члены объявлены в классе). Таким образом, члены
Example
могут быть инициализированы только в следующем порядке:ptr
,name
,pname
,rname
,crname
, иage
.Когда вы не указываете mem-инициализатор члена, стандарт C ++ говорит:
Здесь, поскольку
name
это нестатический член данных типа класса, он инициализируется по умолчанию, еслиname
в списке mem-initializer-list не указан инициализатор для . Все остальные члены классаExample
не имеют типа класса, поэтому они не инициализируются.Когда стандарт говорит, что они не инициализированы, это означает, что они могут иметь любое значение. Таким образом, поскольку приведенный выше код не инициализируется
pname
, это может быть что угодно.Обратите внимание, что вы все равно должны следовать другим правилам, таким как правило, что ссылки всегда должны быть инициализированы. Это ошибка компилятора, чтобы не инициализировать ссылки.
источник
.h
) и определение (in.cpp
), не показывая слишком много внутренних элементов .Вы также можете инициализировать элементы данных в том месте, где вы их объявили:
Я использую эту форму в основном исключительно, хотя я читал, что некоторые люди считают ее «плохой формой», возможно, потому, что она была введена только недавно - я думаю, в C ++ 11. Для меня это более логично.
Еще один полезный аспект новых правил - как инициализировать данные-члены, которые сами являются классами. Например, предположим, что
CDynamicString
это класс, который инкапсулирует обработку строк. Он имеет конструктор, который позволяет указать его начальное значениеCDynamicString(wchat_t* pstrInitialString)
. Вы могли бы очень хорошо использовать этот класс в качестве члена данных внутри другого класса - скажем, класса, который инкапсулирует значение реестра Windows, которое в этом случае хранит почтовый адрес. Чтобы «жестко запрограммировать» имя раздела реестра, в которое это записывается, вы используете фигурные скобки:Обратите внимание, что второй строковый класс, который содержит фактический почтовый адрес, не имеет инициализатора, поэтому его конструктор по умолчанию будет вызываться при создании - возможно, автоматически устанавливая для него пустую строку.
источник
Если ваш пример класса создается в стеке, содержимое неинициализированных скалярных членов является случайным и неопределенным.
Для глобального экземпляра неинициализированные скалярные члены будут обнулены.
Для членов, которые сами являются экземплярами классов, будут вызываться их конструкторы по умолчанию, поэтому ваш строковый объект будет инициализирован.
int *ptr;
// неинициализированный указатель (или обнуляется, если глобальный)string name;
// вызывается конструктор, инициализируется пустой строкойstring *pname;
// неинициализированный указатель (или обнуляется, если глобальный)string &rname;
// ошибка компиляции, если вам не удалось инициализировать этоconst string &crname;
// ошибка компиляции, если вам не удалось инициализировать этоint age;
// скалярное значение, неинициализированное и случайное (или обнуляемое, если оно глобально)источник
string name
он пуст после инициализации класса в стеке. Вы абсолютно уверены в своем ответе?Неинициализированные нестатические члены будут содержать случайные данные. На самом деле, они просто будут иметь значение ячейки памяти, которой они назначены.
Конечно, для параметров объекта (например
string
) конструктор объекта может выполнить инициализацию по умолчанию.В вашем примере:
источник
У членов с конструктором будет вызываться конструктор по умолчанию для инициализации.
Вы не можете зависеть от содержимого других типов.
источник
Если он находится в стеке, содержимое неинициализированных членов, не имеющих собственного конструктора, будет случайным и неопределенным. Даже если это глобально, было бы плохой идеей полагаться на их обнуление. Находится ли он в стеке или нет, если член имеет свой собственный конструктор, он будет вызван для его инициализации.
Итак, если у вас есть строка * pname, указатель будет содержать случайный мусор. но для имени строки будет вызван конструктор по умолчанию для строки, который даст вам пустую строку. Что касается переменных ссылочного типа, я не уверен, но, вероятно, это будет ссылка на некоторый случайный кусок памяти.
источник
Это зависит от того, как построен класс
Ответ на этот вопрос дает понимание огромного оператора case switch в стандарте языка C ++, и простому смертному трудно понять интуицию.
В качестве простого примера того, как сложная вещь:
main.cpp
При инициализации по умолчанию вы начинаете с: https://en.cppreference.com/w/cpp/language/default_initialization, мы переходим к части «Эффекты инициализации по умолчанию» и запускаем оператор case:
Затем, если кто-то решит инициализировать значение, мы перейдем на https://en.cppreference.com/w/cpp/language/value_initialization «Эффекты инициализации значения есть» и запустим оператор case:
= delete
)Вот почему я настоятельно рекомендую вам никогда не полагаться на «неявную» нулевую инициализацию. Если нет веских причин для производительности, явно инициализируйте все, либо на конструкторе, если вы его определили, либо с помощью агрегатной инициализации. В противном случае вы делаете вещи очень и очень рискованными для будущих разработчиков.
источник