上面的例子中两个构造函数的效果是一样的。使用初始化列表的构造函数是显示地初始化类的成员;而没有使用初始化列表的构造函数是对类的成员赋值,并没有显示地初始化。
初始化列表的构造函数和内部赋值的构造函数对内置类型的成员没有什么大的区别,像上面的任一个构造函数都可以。
用构造函数的初始化列表来进行初始化,写法方便、简练,尤其当需要初始化的数据成员较多时更显其优越性。对非内置类型成员变量,推荐使用类构造函数初始化列表。
但有的时候必须用带有初始化列表的构造函数:(1)没有默认构造函数的成员类对象;(2)const成员或引用类型的成员。
构造函数中有着比我们所看见的还要多的细节,构造函数可以调用其它的构造函数来初始化对象中的基类对象和成员对象的构造函数。
类的数据成员中的其它类对象,若该成员类型是没有默认构造函数,则必须进行显示初始化,因为编译器会隐式调用成员类型的默认构造函数,而它又没有默认构造函数,则编译器尝试使用默认构造函数将会失败。
B类数据成员中有一个A类对象a,创建B类对象时,要先创建其成员对象a;A类有一个参数化大的构造函数,则编译器不会提供默认无参数的构造函数,因此a无法创建。
对成员对象正确的初始化方法是通过显示方式进行,B的构造函数应该写成:
B(int y, int z) : a(z) { j = y; } B b(5,10);构造函数初始化列表是初始化常数据成员和引用成员的唯一方式。因为const对象或引用类型只能初始化,不能对他们赋值。
class A { public: A (int x,int y) : c(x),j(y) // 构造函数初始化列表 { i = -1; } private: int i; const int c; int& j; }; int main( ) { int m; A a(5,m); return 0; }若不通过初始化列表来对常数据成员和引用成员进行初始化:
class A { public: A (int x) // 构造函数初始化列表 { i = -1; c = 5; j = x; } private: int i; const int c; // error C2758: “A::c”: 必须在构造函数基/成员初始值设定项列表中初始化 int& j; // error C2758: “A::j”: 必须在构造函数基/成员初始值设定项列表中初始化 };缺省情况下,在构造函数的被执行前,对象中的所有成员都已经被它们的缺省构造函数初始化了。当类中某个数据成员本身也是一个类对象时,我们应该避免使用赋值操作来对该成员进行初始化:
class Person { private: string name; public: Person(string& n) { name = n; } }虽然这样的构造函数也能达到正确的结果,但这样写效率并不高。当一个Person对象创建时,string类成员对象name先会被缺省构造函数进行初始化,然后在Person类构造函数中,它的值又会因赋值操作而在改变一次。我们可以通过初始化列表来显示地对name进行初始化,这样便将上边的两步(初始化和赋值)合并到一个步骤中了。
class Person { private: string name; public: Person(string& n): name(n){} } 多重继承C++的一个主要目标是促进代码重用。公有继承是实现这种目标的机制之一,但并不是唯一的机制。
MI描述的是有多个直接基类的类。与单继承一样,公有MI表示的也是is-a关系。
请注意,必须使用关键字public来限定每一个基类。这是因为,除非特别指出,否则编译器将认为是私有派生。
假设Worker是基类,
class Singer:public Worker{}; class Waiter:public Worker{}; class SingingWaiter : public Singer,public Waiter{};那么将派生类对象的地址赋给基类指针,但现在将出现二义性。
通常,这种赋值将把基类指针设置为派生对象中的基类对象的地址。但ed中包含两个Worker对象,有两个地址可供选择,所以应使用类型转换来指定对象:
is-a即继承关系,子类是父类的特化,如特斯拉是一种车,另外多重继承也是is-a关系;
has-a,子类是父类的组成部分,如轮胎是车的一部分,包含关系通常使用包含和私有继承来实现;
包含类是指类以对象的方式存在于另一个类内,如string temp,有时也会作为成员变量来使用;
嵌套类是指在类内定义类,这时嵌套类只在类内有效,不同于包含关系,嵌套类在类内定义并在类内实例化使用。