如果我没有明确地做,C ++类成员如何被初始化?

假设我有一个私人crname类, namenamenameage crname 。 如果我自己不初始化它会怎么样? 这里是一个例子:

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

然后我做:

 int main() { Example ex; } 

成员如何初始化? 指针会发生什么? stringint是否使用默认的构造函数string()int()来实现0-intialized? 那么参考成员呢? 还有什么关于const引用?

还有什么我应该知道的?

有谁知道涵盖这些情况的教程? 也许在某些书中? 我可以在大学的图书馆访问很多C ++书籍。

我想学习它,所以我可以写更好的(无bug)程序。 任何反馈将帮助!

代替显式初始化,类中成员的初始化与函数中局部variables的初始化相同。

对于对象 ,它们的默认构造函数被调用。 例如,对于std::string ,默认的构造函数将其设置为一个空string。 如果对象的类没有默认构造函数,如果没有明确地初始化它,将会是一个编译错误。

对于原始types (指针,整数等),它们不会被初始化 – 它们包含之前在该内存位置发生的任何垃圾。

对于引用 (例如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 

首先,让我解释一下mem-initializer-list是什么。 一个mem-initializer-list是一个以逗号分隔的mem-initializer列表,其中每个mem-initializer是一个成员名称,后跟( ,后跟一个expression式列表 ,后跟一个)expression式列表是如何构造成员。 例如,在

 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-listname(s_str, s_str + 8), rname(name), crname(name), age(-4) 。 这个mem-initializer-list意味着name成员由std::string构造函数初始化, 该构造函数接受两个input迭代器 , rname成员用name引用初始化, crname成员用const引用初始化为nameage成员初始值为-4

每个构造函数都有它自己的mem-initializer-list ,成员只能按照规定的顺序(基本上是在类中声明成员的顺序)初始化。 因此, Example的成员只能按照ptrnamepnamernamecrnameage的顺序初始化。

当你不指定一个成员的mem-initializer时,C ++标准说:

如果实体是类types…的非静态数据成员…,则该实体将被默认初始化(8.5)。 …否则,实体未初始化。

这里,因为name是类types的非静态数据成员,所以如果没有在mem-initializer-list中指定name的初始化方法,那么它是默认初始化的Example所有其他成员都没有类的types,所以它们没有被初始化。

当标准说他们没有初始化,这意味着他们可以有任何价值。 因此,因为上面的代码没有初始化pname ,所以可能是任何东西。

请注意,您仍然需要遵循其他规则,例如必须始终初始化引用的规则。 不初始化引用是编译器错误。

如果您的示例类在堆栈上实例化,则未初始化的标量成员的内容是随机的并且是未定义的。

对于全局实例,未初始化的标量成员将被清零。

对于本身是类实例的成员,它们的默认构造函数将被调用,所以你的string对象将被初始化。

  • int *ptr; //未初始化的指针(如果是全局的,则为零)
  • string name; //调用构造函数,用空string初始化
  • string *pname; //未初始化的指针(如果是全局的,则为零)
  • string &rname; / /编译错误,如果你不能初始化这个
  • const string &crname; / /编译错误,如果你不能初始化这个
  • int age; //标量值,未初始化和随机(或全局归零)

您还可以在声明它们的位置初始化数据成员:

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

尽pipe我已经阅读了一些人认为它是“不好的forms”,可能是因为它只是最近才引入的 – 我认为在C ++ 11中。 对我来说更合乎逻辑。

新规则的另一个有用的方面是如何初始化本身是类的数据成员。 比如假设CDynamicString是一个封装string处理的类。 它有一个构造函数,允许你指定它的初始值CDynamicString(wchat_t* pstrInitialString) 。 你可能很好地使用这个类作为另一个类中的数据成员 – 比如说封装了一个windowsregistry值的类,在这种情况下,它存储一个邮政地址。 要“硬编码”registry项的名称,使用花括号:

 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; }; 

注意保存实际邮政地址的第二个string类没有初始化程序,所以它的默认构造函数将在创build时调用 – 也许会自动将其设置为空string。

未初始化的非静态成员将包含随机数据。 实际上,它们只是具有分配给它们的内存位置的值。

当然,对于对象参数(如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 

具有构造函数的成员将具有要求初始化的默认构造函数。

你不能依赖于其他types的内容。

如果它在堆栈上,没有自己的构造函数的未初始化成员的内容将是随机的和未定义的。 即使是全球性的,依靠他们被淘汰也不是一个好主意。 无论是否在堆栈中,如果一个成员有自己的构造函数,它将被调用来初始化它。

所以,如果你有string* pname,指针将包含随机垃圾。 但是对于string名称,string的默认构造函数将被调用,给你一个空string。 对于你的引用typesvariables,我不确定,但它可能是一些随机的内存块的参考。