如果我没有明确地做,C ++类成员如何被初始化?
假设我有一个私人crname
类, name
, name
, name
, age
crname
。 如果我自己不初始化它会怎么样? 这里是一个例子:
class Example { private: int *ptr; string name; string *pname; string &rname; const string &crname; int age; public: Example() {} };
然后我做:
int main() { Example ex; }
成员如何初始化? 指针会发生什么? string
和int
是否使用默认的构造函数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-list是name(s_str, s_str + 8), rname(name), crname(name), age(-4)
。 这个mem-initializer-list意味着name
成员由std::string
构造函数初始化, 该构造函数接受两个input迭代器 , rname
成员用name
引用初始化, crname
成员用const引用初始化为name
, age
成员初始值为-4
。
每个构造函数都有它自己的mem-initializer-list ,成员只能按照规定的顺序(基本上是在类中声明成员的顺序)初始化。 因此, Example
的成员只能按照ptr
, name
, pname
, rname
, crname
和age
的顺序初始化。
当你不指定一个成员的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,我不确定,但它可能是一些随机的内存块的参考。