目录
成员初始化列表语法
01)问题的提出:
对于一个Queue类的声明(不存在问题,注意在类中声明一个常量):
class Queue
{
private:
int items;
const int qsize; //常量
pubilc:
Queue(int qs); //构造函数
...
};
Queue类构造函数的定义(存在问题,因为不能给常量qsize赋值):
Queue::Queue(int qs)
{
items = 0;
qsize = 0; //不合法!
}
02)解决的方法:使用成员初始化列表
Classy::Classy(int n,int m) : mem1(n), mem2(0), mem3(n*m+2)
{
...
}
以上将n的值赋给mem1,将0赋给mem2,将n*m+2的值赋给mem3。从概念上说,这些初始化构造是在对象创建时完成的,此时还未执行
大括号中的任何代码。请注意一下几点:
A 成员初始化列表只能用于构造函数
B 必须用这种格式来初始化非静态const数据成员
C 必须用这种格式来初始化引用数据成员
D 不能讲成员初始化列表用于构造函数之外的其他类方法
E 成员初始化列表的括号方式也可以用于常规舒适化,可以将下述代码:
int games = 162;
double talk = 2.71828;
替换为:
int games(162);
double talk(2.71828);
03)C++11的类内初始化:
class Classy
{
int mem1 = 10;
const int mem2 = 20;
};
与在构造函数中使用成员初始化列表等价:
Classy::Classy() : mem1(10), mem2(20) { ... }
也可以将初始值替换为其他的数值:
Classy::Classy(int n) : mem1(n) { ... } //此时mem1=n, mem2=20
派生类 m2
01)加入有一个类TableTennisPlayer类,在这个类的基础上派生出一个类RatedPlayer,方法如下
class RatedPlayer : public TableTennisPlayer //公有继承方式(public) TableTennisPlayer为基类,RatedPlayer 为派生类
{
private:
...
public:
...
};
02)上述语句是写在h文件中的;冒号指出RatedPlayer是派生类,TableTennisPlayer是基类
派生类对象储存了基类的数据成员(派生类继承了基类的实现);
派生类对象可以使用基类的方法(派生类继承了基类的接口);
派生类需要自己的构造函数,可以根据需要添加额外的数据成员和成员函数;
03)派生类不可以直接访问基类的私有数据成员,而必须通过基类方法进行访问,派生类构造函数必须使用基类构造函数,
因为要肯定是要创建派生类对象的,那么在创建派生类对象的时候,也必须加上基类的私有数据,所以派生类构造函数必须使用基类构造函数
对基类中的数据成员进行赋值;
创建培生类对象时,必须首先创建基类对象。从概念上说,这意味着基类对象应该在程序进入派生类构造函数之前被创建,那么我们使用
成员初始化列表的语法来完成这项工作,例如:
RatedPlayer::RatedPlayer(unsiged int r,const string & fn,const string & ln,bool ht) : TableTennisPlayer(fn,ln,ht)
{
rating = r;
};//该语句是在对应的类cpp文件中
I 最后的TableTennisPlayer(fn,ln,ht)表示首先创建基类对象,并对该对象进行赋值,即将fn,ln,ht赋给基类对应的参数
II 在主函数文件中将可以执行如下代码:
RatedPlayer ratedPlayer(1140,"Mallory","Duck",true); //创建派生类对象ratedPlayer
RatedPlayer构造函数将把实参"Mallory","Duck",true赋给形参fn,ln,ht,然后将这些参数作为实参传递给TableTennisPlayer构造函数对象
并将数据"Mallory","Duck",true存储在该对象中。然后进入RatedPlayer构造函数体,完成ratedPlayer的创建
III 如果省略成员初始化列表,那么程序将会使用基类的默认构造函数,完成数据的赋值操作
04)来看第二个派生类构造函数代码:
RatedPlayer::RatedPlayer(unsiged int r,const TableTennisPlayer & tp) : TableTennisPlayer(tp)
{
rating = r;
}
由于tp的类型为指向TableTennisPlayer类对象的引用,因此将调用基类的复制构造函数。基类如果没有定义赋值构造函数,则编译器
将自动生成一个。由于该例程中没有使用动态内存分配(string成员确实是使用了动态内存分配,但是成员复制将使用string类的复制构造函数
来复制string成员),所以使用编译器自动生成的复制构造函数是可行的。
05)如果愿意,也可以使用成员初始化列表的方法对派生类成员进行初始化,在这时,应该使用成员名(rating),而不是类名,如下:
RatedPlayer::RatedPlayer(unsiged int r,const TableTennisPlayer & tp) : TableTennisPlayer(tp),rating(r)
{
}
06)有关派生类构造函数的要点如下:
I 首先要创建基类对象;
II 派生类构造函数应该通过成员初始化列表将基类信息传递给基类构造函数;
III 派生类构造函数应初始化派生类新增的数据成员;
IV 创建派生类对象时,程序首先调用基类构造函数,然后再调用派生类构造函数。基类构造函数负责初始化继承的数据成员;派生类
构造函数负责初始化新增的数据成员。派生类构造函数总是调用一个基类构造函数,可以使用成员初始化列表的方法指明要使用的
基类构造函数,否则将使用默认的基类构造函数;
IIV 派生类对象过期时,程序将首先调用派生类析构函数,然后再调用基类析构函数;
IIIV 初始化成员列表只能用于构造函数。
1 /* 派生类 */ 2 /* 3 01)加入有一个类TableTennisPlayer类,在这个类的基础上派生出一个类RatedPlayer,方法如下 4 class RatedPlayer : TableTennisPlayer 5 { 6 private: 7 ... 8 public: 9 ... 10 }; 11 02)上述语句是写在h文件中的;冒号指出RatedPlayer是派生类,TableTennisPlayer是基类 12 派生类对象储存了基类的数据成员(派生类继承了基类的实现); 13 派生类对象可以使用基类的方法(派生类继承了基类的接口); 14 派生类需要自己的构造函数,可以根据需要添加额外的数据成员和成员函数; 15 03)派生类不可以直接访问基类的私有数据成员,而必须通过基类方法进行访问,派生类构造函数必须使用基类构造函数, 16 因为要肯定是要创建派生类对象的,那么在创建派生类对象的时候,也必须加上基类的私有数据,所以派生类构造函数必须使用基类构造函数 17 对基类中的数据成员进行赋值; 18 创建培生类对象时,必须首先创建基类对象。从概念上说,这意味着基类对象应该在程序进入派生类构造函数之前被创建,那么我们使用 19 成员初始化列表的语法来完成这项工作,例如: 20 RatedPlayer::RatedPlayer(unsiged int r,const string & fn,const string & ln,bool ht) : TableTennisPlayer(fn,ln,ht) 21 { 22 rating = r; 23 };//该语句是在对应的类cpp文件中 24 I 最后的TableTennisPlayer(fn,ln,ht)表示首先创建基类对象,并对该对象进行赋值,即将fn,ln,ht赋给基类对应的参数 25 II 在主函数文件中将可以执行如下代码: 26 RatedPlayer ratedPlayer(1140,"Mallory","Duck",true); //创建派生类对象ratedPlayer 27 RatedPlayer构造函数将把实参"Mallory","Duck",true赋给形参fn,ln,ht,然后将这些参数作为实参传递给TableTennisPlayer构造函数对象 28 并将数据"Mallory","Duck",true存储在该对象中。然后进入RatedPlayer构造函数体,完成ratedPlayer的创建 29 III 如果省略成员初始化列表,那么程序将会使用基类的默认构造函数,完成数据的赋值操作 30 04)来看第二个派生类构造函数代码: 31 RatedPlayer::RatedPlayer(unsiged int r,const TableTennisPlayer & tp) : TableTennisPlayer(tp) 32 { 33 rating = r; 34 } 35 由于tp的类型为指向TableTennisPlayer类对象的引用,因此将调用基类的复制构造函数。基类如果没有定义赋值构造函数,则编译器 36 将自动生成一个。由于该例程中没有使用动态内存分配(string成员确实是使用了动态内存分配,但是成员复制将使用string类的复制构造函数 37 来复制string成员),所以使用编译器自动生成的复制构造函数是可行的。 38 05)如果愿意,也可以使用成员初始化列表的方法对派生类成员进行初始化,在这时,应该使用成员名(rating),而不是类名,如下: 39 RatedPlayer::RatedPlayer(unsiged int r,const TableTennisPlayer & tp) : TableTennisPlayer(tp),rating(r) 40 { 41 42 } 43 06)有关派生类构造函数的要点如下: 44 I 首先要创建基类对象; 45 II 派生类构造函数应该通过成员初始化列表将基类信息传递给基类构造函数; 46 III 派生类构造函数应初始化派生类新增的数据成员; 47 IV 创建派生类对象时,程序首先调用基类构造函数,然后再调用派生类构造函数。基类构造函数负责初始化继承的数据成员;派生类 48 构造函数负责初始化新增的数据成员。派生类构造函数总是调用一个基类构造函数,可以使用成员初始化列表的方法指明要使用的 49 基类构造函数,否则将使用默认的基类构造函数; 50 IIV 派生类对象过期时,程序将首先调用派生类析构函数,然后再调用基类析构函数; 51 IIIV 初始化成员列表只能用于构造函数。 52 */ 53 54 #ifndef TABTENN1_H_ 55 #define TABTENN1_H_ 56 57 #include <string> 58 59 using std::string; 60 61 /* 基类的声明 */ 62 class TableTennisPlayer 63 { 64 private: 65 string firstname; 66 string lastname; 67 bool hasTable; //是否有桌子 68 public: 69 TableTennisPlayer(const string & fn = "none",const string & ln = "none", bool ht = false); //带默认参数的构造函数 70 //由于"none"是const char类型的,故要fn的类型也要为const类型 71 void ShowName() const; //最后一个const表示不可以对对象中的数据进行修改 72 bool HasTable() const { return hasTable; } //返回私有数据,进而可以使对象可以访问私有数据 73 void ResetTable(bool v) { hasTable = v; } //对是否有球桌进行重新选择 74 }; 75 /* 派生类 */ 76 class RatedPlayer : public TableTennisPlayer //表明TableTennisPlayer是基类,RatedPlayer是派生类 77 { 78 private: 79 unsigned int rating; //派生类新增的数据成员 80 public: 81 RatedPlayer(unsigned int r = 0, const string & fn = "none", const string & ln = "none", bool ht = false); //RatedPlayer构造函数,以具体数据的方式将数据传递给基类构造函数 82 RatedPlayer(unsigned int r, const TableTennisPlayer & tp); //以指向基类对象的引用作为参数传递给基类构造函数 83 unsigned int Rating() const { return rating; } //返回私有数据,进而可以使对象可以访问私有数据 84 void ResetRating(unsigned int r) { rating = r; } //派生类新增的类方法 85 }; 86 87 88 #endif
1 //tabletenn1.cpp 2 3 #include "tabletenn1.h" 4 #include <iostream> 5 6 using std::cout; 7 using std::endl; 8 9 /* 基类构造函数(使用初始化成员列表方法初始化数据成员) 10 01)注意啦,构造函数前也是要加类限定符TableTennisPlayer::的好不啦!!! 11 02)注意在h文件中的默认参数是不可以带过来的啦!!! 12 */ 13 TableTennisPlayer::TableTennisPlayer(const string & fn, const string & ln, bool ht) 14 :firstname(fn), lastname(ln), hasTable(ht) {} 15 //将fn赋值给私有数据firstname,ln赋值给lastname,ht赋值给hasTable,函数体为空 16 17 void TableTennisPlayer::ShowName() const 18 { 19 cout << lastname << ", " << firstname; 20 } 21 22 /* 派生类构造函数 */ 23 RatedPlayer::RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht) 24 :TableTennisPlayer(fn,ln,ht) 25 { 26 rating = r; 27 } 28 //首先使用基类构造函数TableTennisPlayer(const string & fn = "none", const string & ln = "none", bool ht = false)将数据传入基类中 29 //然后创建派生类对象,最后执行rating = r; 30 RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp) 31 :TableTennisPlayer(tp),rating(r) 32 { 33 34 } 35 //首先使用基类构造函数TableTennisPlayer(const string & fn = "none", const string & ln = "none", bool ht = false)将数据传入基类中 36 //tp是指向基类对象的形参,实参赋值给形参,会调用基类的复制构造函数,如果没有使用new(在基类中可以不定义复制构造函数),将使用基类的默认复制构造函数也是可以的 37 //然后创建派生类对象,最后执行rating = r; 38 /* 39 也可以这样写: 40 RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp) 41 :TableTennisPlayer(tp) 42 { 43 rating = r; 44 } 45 */
1 #include <iostream> 2 #include "tabletenn1.h" 3 4 int main() 5 { 6 using std::cout; 7 using std::endl; 8 9 TableTennisPlayer player1("Tara", "Boomadea", false); //创建基类对象player1 10 RatedPlayer rplayer1(1140, "Mallory", "Duck", true); //创建派生类对象rplayer1 11 12 player1.ShowName(); //基类对象调用基类方法 13 if (player1.HasTable()) 14 cout << ": has a table" << endl; 15 else 16 cout << ": hasn't a table" << endl; 17 18 rplayer1.ShowName(); //派生类对象调用基类方法 19 if(rplayer1.HasTable()) 20 cout << ": has a table" << endl; 21 else 22 cout << ": hasn't a table" << endl; 23 24 cout << "Name: "; 25 rplayer1.ShowName(); 26 cout << "; Rating: " << rplayer1.Rating() << endl; 27 28 RatedPlayer rplayer2(1212, player1); //使用基类对象player1作为实参创建派生类对象rplayer2 29 cout << "Name: "; 30 rplayer2.ShowName(); 31 cout << "; Rating: " << rplayer2.Rating() << endl; 32 33 system("pause"); 34 return 0; 35 36 }
执行结果: