构造函数的执行分为两个阶段:
初始化阶段
主体执行阶段,由构造函数主体中的所有语句组成。请注意,类类型的数据成员总是在初始化阶段初始化的,无论该成员是否在构造函数初始值设定项列表中显式初始化。初始化发生在构造函数主体的任何语句执行之前。
让我们考虑一种通过构造函数初始化类学生实例的方法
Student(string &fn, string &ln, int i, int y = Freshman)
: first_name(fn)
, last_name(ln)
, id(i)
, year(y)
{}
这是另一种"低效"且"不雅"的方法——
Student(string &fn, string &ln, int i, int y = Freshman)
{
first_name = fn;
last_name = ln;
id = i;
year = y;
}
新代码中的这个构造函数(上面的代码(分配类Student的成员。它不会显式初始化它们。无论是否存在显式初始值设定项,first_name和last_name成员都会在执行构造函数之前进行初始化。此构造函数隐式使用默认字符串构造函数来初始化first_name和last_name成员。在执行构造函数的主体时,first_name和last_name成员已经具有值。这些值会被构造函数体内部的赋值覆盖。
因此,这意味着当执行到达构造函数的左括号时,这就是条件-
- "first_name"是字符串,通过调用默认字符串构造函数(编译器">生成的构造函数(进行初始化
- "last_name"是一个字符串,通过调用默认字符串构造函数(编译器">生成的构造函数(进行初始化
- "id"是整数,未初始化
- "year"是整数,未初始化
现在,很明显,赋值是对构造函数主体中的四个变量中的每一个进行的。
我对这件事的理解正确吗?不知怎么的,我觉得我错过了什么
此外,在使用初始化列表时,默认构造函数(编译器为我们制作的构造函数(是否被调用并与我们的参数一起传递(在字符串的情况下(,以及在id(i(的情况下是否如"int id=i;">中那样进行初始化??
PS:大多数报价都来自这个链接-
http://www.bogotobogo.com/cplusplus/constructor.php
您的理解基本上是正确的,只是您必须在初始化器中初始化const
成员,因为您无法在构造函数的主体或其他任何位置为它们赋值(因为它们是const
(。如果您试图使const
成员未初始化,则会出现编译错误。
是的,在初始化列表中,会调用与给定参数匹配的对象的构造函数。如果没有参数(例如: blah()
(,则调用默认构造函数。
另外,在某些情况下,绝对必须使用初始化程序列表(这不是可选(,例如
- 当类具有
const
成员时 - 当类是没有默认构造函数的类的直接子级时
- 当类具有引用成员时
您并不是在说Student
类是如何定义的,但您可能会对一件事感到困惑:在第二个"不雅"版本中,first_name
不一定是const字符串,相反,fn
是对const字符串的引用,您将的值分配给成员变量first_name
。
现在,转到初始值设定项列表:它们的全部意义在于您可以指定调用成员对象的构造函数。如果没有列表,所有成员对象都会调用其默认构造函数(或者更准确地说,它们是默认初始化的(。但是,您在初始值设定项列表中提到的任何对象都将具有,而构造函数(或者更确切地说是"初始值设定者"(将调用而不是。
初始值设定项列表不仅仅是装饰。这是至关重要的,因为成员对象和子对象甚至可能不是默认的可构造对象,因此必须提供初始值设定项。const成员实际上是一个很好的例子:
struct Foo
{
const int n;
Foo(); // error! What is n?
Foo(int m) : n(m) { } // OK
};
下面是另一个例子,这次是从一个没有默认构造函数的基类派生的:
struct Bar : Foo
{
Bar(); // error! How to initialize Foo?
Bar() : Foo(5) { } // OK, now Bar::n == 5
};
此外,在使用初始化列表时,默认构造函数(编译器为我们制作的构造函数(是否被调用并与我们的参数一起传递(在字符串的情况下(,以及在id(i(的情况下是否按照"int id=i;"中的方式进行初始化??
首先,默认构造函数不一定是"编译器为我们制作的构造函数"。它们只是没有参数的构造函数,例如Student()
。有时编译器会自动生成一个,比如int
,有时它必须被编写,比如std::string
。
考虑它的简单方法是:如果存在初始值设定项,则调用相应的构造函数。因此,假设first_name
是std::string
,则初始化器first_name(fn)
导致用std::string::string(const std::string&)
构造first_name
,并传递fn
作为参数。如果初始值设定项未使用,则在执行{ ... }
代码之前,first_name
是用std::string::string()
构造的,然后在编写first_name = fn;
时,将导致对std::string::operator =(const std::string&)
的调用,并将fn
作为参数传递。
最后,关于初始化器,需要实现的重要一点是,它们按照成员在类中声明的顺序执行,而不是按照在构造函数中写入它们的顺序执行。此外,基类构造函数的初始化器发生在成员初始化之前。