如果我不明确地初始化 C++ 类成员如何初始化?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3127454/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do C++ class members get initialized if I don't do it explicitly?
提问by bodacydo
Suppose I have a class with private memebers ptr
, name
, pname
, rname
, crname
and age
. What happens if I don't initialize them myself? Here is an example:
假设我有私人承包商,客人一类ptr
,name
,pname
,rname
,crname
和age
。如果我自己不初始化它们会怎样?下面是一个例子:
class Example {
private:
int *ptr;
string name;
string *pname;
string &rname;
const string &crname;
int age;
public:
Example() {}
};
And then I do:
然后我做:
int main() {
Example ex;
}
How are the members initialized in ex? What happens with pointers? Do string
and int
get 0-intialized with default constructors string()
and int()
? What about the reference member? Also what about const references?
ex中的成员是如何初始化的?指针会发生什么?Dostring
并int
使用默认构造函数string()
和int()
? 进行0 初始化。参考成员呢?还有 const 引用呢?
What else should I know about?
我还应该知道什么?
Does anyone know a tutorial that covers these cases? Maybe in some books? I have access in university's library to a lot of C++ books.
有谁知道涵盖这些情况的教程?也许在某些书中?我可以在大学图书馆访问很多 C++ 书籍。
I'd like to learn it so I can write better (bug free) programs. Any feedback would help!
我想学习它,以便我可以编写更好的(无错误)程序。任何反馈都会有所帮助!
回答by Tyler McHenry
In lieu of explicit initialization, initialization of members in classes works identically to initialization of local variables in functions.
代替显式初始化,类中成员的初始化与函数中局部变量的初始化工作相同。
For objects, their default constructor is called. For example, for std::string
, the default constructor sets it to an empty string. If the object's class does not have a default constructor, it will be a compile error if you do not explicitly initialize it.
对于objects,它们的默认构造函数被调用。例如, for std::string
,默认构造函数将其设置为空字符串。如果对象的类没有默认构造函数,那么如果没有显式初始化它,就会出现编译错误。
For primitive types(pointers, ints, etc), they are notinitialized -- they contain whatever arbitrary junk happened to be at that memory location previously.
对于原始类型(指针、整数等),它们没有被初始化——它们包含之前在该内存位置发生的任何任意垃圾。
For references(e.g. std::string&
), it is illegalnot to initialize them, and your compiler will complain and refuse to compile such code. References must always be initialized.
对于引用(例如std::string&
),不初始化它们是非法的,您的编译器会抱怨并拒绝编译此类代码。必须始终初始化引用。
So, in your specific case, if they are not explicitly initialized:
因此,在您的特定情况下,如果它们未显式初始化:
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
回答by Daniel Trebbien
First, let me explain what a mem-initializer-listis. A mem-initializer-listis a comma-separated list of mem-initializers, where each mem-initializeris a member name followed by (
, followed by an expression-list, followed by a )
. The expression-listis how the member is constructed. For example, in
首先,让我解释一下mem-initializer-list是什么。甲MEM-初始化列表是逗号分隔的列表MEM-初始化s,其中每个MEM-初始化是其成员的名字,随后(
,接着是表达式列表,随后进行)
。的表达式列表是构件是如何构造的。例如,在
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)
{
}
};
the mem-initializer-listof the user-supplied, no-arguments constructor is name(s_str, s_str + 8), rname(name), crname(name), age(-4)
. This mem-initializer-listmeans that the name
member is initialized by the std::string
constructor that takes two input iterators, the rname
member is initialized with a reference to name
, the crname
member is initialized with a const-reference to name
, and the age
member is initialized with the value -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
成员使用对的常量引用初始化name
,并且age
成员使用值初始化-4
。
Each constructor has its own mem-initializer-list, and members can only be initialized in a prescribed order (basically the order in which the members are declared in the class). Thus, the members of Example
can only be initialized in the order: ptr
, name
, pname
, rname
, crname
, and age
.
每个构造函数都有自己的mem-initializer-list,并且成员只能按照规定的顺序(基本上是成员在类中声明的顺序)进行初始化。因此,成员Example
只能在顺序进行初始化:ptr
,name
,pname
,rname
,crname
,和age
。
When you do not specify a mem-initializerof a member, the C++ standard says:
当您不指定成员的mem-initializer时,C++ 标准说:
If the entity is a nonstatic data member ... of class type ..., the entity is default-initialized (8.5). ... Otherwise, the entity is not initialized.
如果实体是类类型的非静态数据成员...,则实体是默认初始化的 (8.5)。... 否则,实体不会被初始化。
Here, because name
is a nonstatic data member of class type, it is default-initialized if no initializer for name
was specified in the mem-initializer-list. All other members of Example
do not have class type, so they are not initialized.
在这里,因为name
是类类型的非静态数据成员,所以如果name
在mem-initializer-list 中没有指定初始化器,它是默认初始化的。的所有其他成员Example
都没有类类型,因此它们没有被初始化。
When the standard says that they are not initialized, this means that they can have anyvalue. Thus, because the above code did not initialize pname
, it could be anything.
当标准说它们未初始化时,这意味着它们可以具有任何值。因此,因为上面的代码没有初始化pname
,它可以是任何东西。
Note that you still have to follow other rules, such as the rule that references must always be initialized. It is a compiler error to not initialize references.
请注意,您仍然必须遵循其他规则,例如必须始终初始化引用的规则。不初始化引用是编译器错误。
回答by Daniel Trebbien
You can also initialize data members at the point you declare them:
您还可以在声明数据成员时初始化它们:
class another_example{
public:
another_example();
~another_example();
private:
int m_iInteger=10;
double m_dDouble=10.765;
};
I use this form pretty much exclusively, although I have read some people consider it 'bad form', perhaps because it was only recently introduced - I think in C++11. To me it is more logical.
我几乎只使用这种形式,虽然我读过一些人认为它“糟糕的形式”,也许是因为它最近才被引入——我认为是在 C++11 中。对我来说,这更合乎逻辑。
Another useful facet to the new rules is how to initialize data-members that are themselves classes. For instance suppose that CDynamicString
is a class that encapsulates string handling. It has a constructor that allows you specify its initial value CDynamicString(wchat_t* pstrInitialString)
. You might very well use this class as a data member inside another class - say a class that encapsulates a windows registry value which in this case stores a postal address. To 'hard code' the registry key name to which this writes you use braces:
新规则的另一个有用方面是如何初始化本身就是类的数据成员。例如,假设这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;
};
Note the second string class which holds the actual postal address does not have an initializer so its default constructor will be called on creation - perhaps automatically setting it to a blank string.
请注意,保存实际邮政地址的第二个字符串类没有初始值设定项,因此将在创建时调用其默认构造函数 - 可能会自动将其设置为空字符串。
回答by Paul Dixon
If you example class is instantiated on the stack, the contents of uninitialized scalar members is random and undefined.
如果示例类在堆栈上实例化,则未初始化的标量成员的内容是随机且未定义的。
For a global instance, uninitialized scalar members will be zeroed.
对于全局实例,未初始化的标量成员将被清零。
For members which are themselves instances of classes, their default constructors will be called, so your string object will get initialized.
对于本身是类实例的成员,它们的默认构造函数将被调用,因此您的字符串对象将被初始化。
int *ptr;
//uninitialized pointer (or zeroed if global)string name;
//constructor called, initialized with empty stringstring *pname;
//uninitialized pointer (or zeroed if global)string &rname;
//compilation error if you fail to initialize thisconst string &crname;
//compilation error if you fail to initialize thisint age;
//scalar value, uninitialized and random (or zeroed if global)
int *ptr;
//未初始化的指针(如果是全局的则归零)string name;
//构造函数被调用,用空字符串初始化string *pname;
//未初始化的指针(如果是全局的则归零)string &rname;
//如果初始化失败则编译错误const string &crname;
//如果初始化失败则编译错误int age;
//标量值,未初始化和随机(如果全局则为零)
回答by Wizard
Uninitialized non-static members will contain random data. Actually, they will just have the value of the memory location they are assigned to.
未初始化的非静态成员将包含随机数据。实际上,它们只会拥有分配给它们的内存位置的值。
Of course for object parameters (like string
) the object's constructor could do a default initialization.
当然,对于对象参数(如string
),对象的构造函数可以进行默认初始化。
In your example:
在你的例子中:
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
回答by janm
Members with a constructor will have their default constructor called for initialisation.
具有构造函数的成员将调用其默认构造函数进行初始化。
You cannot depend on the contents of the other types.
您不能依赖其他类型的内容。
回答by George
If it is on the stack, the contents of uninitialized members that don't have their own constructor will be random and undefined. Even if it is global, it would be a bad idea to rely on them being zeroed out. Whether it is on the stack or not, if a member has its own constructor, that will get called to initialize it.
如果它在堆栈上,没有自己的构造函数的未初始化成员的内容将是随机的和未定义的。即使它是全球性的,依靠它们被清零也是一个坏主意。无论它是否在堆栈上,如果一个成员有自己的构造函数,就会调用它来初始化它。
So, if you have string* pname, the pointer will contain random junk. but for string name, the default constructor for string will be called, giving you an empty string. For your reference type variables, I'm not sure, but it'll probably be a reference to some random chunk of memory.
因此,如果您有 string* pname,则指针将包含随机垃圾。但是对于字符串名称,将调用字符串的默认构造函数,为您提供一个空字符串。对于您的引用类型变量,我不确定,但它可能是对一些随机内存块的引用。