深入解析C++中派生类的构造函数
基类的构造函数不能被继承,在声明派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数来完成。所以在设计派生类的构造函数时,不仅要考虑派生类新增的成员变量,还要考虑基类的成员变量,要让它们都被初始化。 解决这个问题的思路是:在执行派生类的构造函数时,调用基类的构造函数。 下面的例子展示了如何在派生类的构造函数中调用基类的构造函数。 #include<iostream> using namespace std; //基类 class People{ protected: char *name; int age; public: People(char*,int); }; People::People(char *name,int age): name(name),age(age){} //派生类 class Student: public People{ private: float score; public: Student(char*,int,float); void display(); }; //调用了基类的构造函数 Student::Student(char *name,int age,float score): People(name,age){ this->score = score; } void Student::display(){ cout<<name<<"的年龄是"<<age<<",成绩是"<<score<<endl; } int main(){ Student stu("小明",16,90.5); stu.display(); return 0; } 运行结果为: 请注意代码第23行: Student::Student(char *name,age)
实际上,你可以将对基类构造函数的调用和参数初始化表放在一起,如下所示: Student::Student(char *name,age),score(score){}
需要注意的是:冒号后面是对基类构造函数的调用,而不是声明,所以括号里的参数是实参,它们不但可以是派生类构造函数总参数表中的参数,还可以是局部变量、常量等。如下所示: Student::Student(char *name,float score): People("李磊",20)
事实上,通过派生类创建对象时必须要调用基类的构造函数,这是语法规定。也就是说,定义派生类构造函数时最好指明基类构造函数;如果不指明,就调用基类的默认构造函数(不带参数的构造函数);如果没有默认构造函数,那么编译失败。 请看下面的例子: #include<iostream> using namespace std; //基类 class People{ protected: char *name; int age; public: People(); People(char*,int); }; People::People(){ this->name = "xxx"; this->age = 0; } People::People(char *name,age(age){} //派生类 class Student: public People{ private: float score; public: Student(); Student(char*,float); void display(); }; Student::Student(){ this->score = 0.0; } Student::Student(char *name,age){ this->score = score; } void Student::display(){ cout<<name<<"的年龄是"<<age<<",成绩是"<<score<<endl; } int main(){ Student stu1; stu1.display(); Student stu2("小明",90.5); stu2.display(); return 0; } 运行结果: xxx的年龄是0,成绩是0 小明的年龄是16,成绩是90.5
创建对象 stu1 时,执行派生类的构造函数 Student::Student(),它并没有指明要调用基类的哪一个构造函数,从运行结果可以很明显地看出来,系统默认调用了不带参数的构造函数,也就是 People::People()。 创建对象 stu2 时,执行派生类的构造函数 Student::Student(char *name,float score),它指明了基类的构造函数。 在第31行代码中,如果将 People(name,age) 去掉,也会调用默认构造函数,stu2.display() 的输出结果将变为: 如果将基类 People 中不带参数的构造函数删除,那么会发生编译错误,因为创建对象 stu1 时没有调用基类构造函数。 总结:如果基类有默认构造函数,那么在派生类构造函数中可以不指明,系统会默认调用;如果没有,那么必须要指明,否则系统不知道如何调用基类的构造函数。 为了搞清这个问题,我们不妨先来看一个例子: #include<iostream> using namespace std; //基类 class People{ protected: char *name; int age; public: People(); People(char*,int); }; People::People(): name("xxx"),age(0){ cout<<"PeoPle::People()"<<endl; } People::People(char *name,age(age){ cout<<"PeoPle::People(char *,int)"<<endl; } //派生类 class Student: public People{ private: float score; public: Student(); Student(char*,float); }; Student::Student(): score(0.0){ cout<<"Student::Student()"<<endl; } Student::Student(char *name,score(score){ cout<<"Student::Student(char*,float)"<<endl; } int main(){ Student stu1; cout<<"--------------------"<<endl; Student stu2("小明",90.5); return 0; } 运行结果: PeoPle::People() Student::Student() -------------------- PeoPle::People(char *,int) Student::Student(char*,float) 从运行结果可以清楚地看到,当创建派生类对象时,先调用基类构造函数,再调用派生类构造函数。如果继承关系有好几层的话,例如: C++有子对象的派生类的构造函数 Student s1; //Student是已声明的类名,s1是Student类的对象
[例] 包含子对象的派生类的构造函数。为了简化程序以易于阅读,这里设基类Student的数据成员只有两个,即num和name。 #include <iostream> #include <string> using namespace std; class Student//声明基类 { public: //公用部分 Student(int n,string nam ) //基类构造函数,与例11.5相同 { num=n; name=nam; } void display( ) //成员函数,输出基类数据成员 { cout<<"num:"<<num<<endl<<"name:"<<name<<endl; } protected: //保护部分 int num; string name; }; class Student1: public Student //声明公用派生类Student1 { public: Student1(int n,string nam,int n1,string nam1,int a,string ad):Student(n,nam),monitor(n1,nam1) //派生类构造函数 { age=a; addr=ad; } void show( ) { cout<<"This student is:"<<endl; display(); //输出num和name cout<<"age: "<<age<<endl; //输出age cout<<"address: "<<addr<<endl<<endl; //输出addr } void show_monitor( ) //成员函数,输出子对象 { cout<<endl<<"Class monitor is:"<<endl; monitor.display( ); //调用基类成员函数 } private: //派生类的私有数据 Student monitor; //定义子对象(班长) int age; string addr; }; int main( ) { Student1 stud1(10010,"Wang-li",10001,"Li-sun",19,"115 Beijing Road,Shanghai"); stud1.show( ); //输出学生的数据 stud1.show_monitor(); //输出子对象的数据 return 0; } 运行时的输出如下: This student is: num: 10010 name: Wang-li age: 19 address:115 Beijing Road,Shanghai Class monitor is: num:10001 name:Li-sun 请注意在派生类Student1中有一个数据成员: Student monitor; //定义子对象 monitor(班长) “班长”的类型不是简单类型(如int、char、float等),它是Student类的对象。我们知道, 应当在建立对象时对它的数据成员初始化。那么怎样对子对象初始化呢?显然不能在声明派生类时对它初始化(如Student monitor(10001,"Li-fun");),因为类是抽象类型,只是一个模型,是不能有具体的数据的,而且每一个派生类对象的子对象一般是不相同的(例如学生A、B、C的班长是A,而学生D、E、F的班长是F)。因此子对象的初始化是在建立派生类时通过调用派生类构造函数来实现的。 派生类构造函数的任务应该包括3个部分:
程序中派生类构造函数首部如下: Student1(int n,string ad): Student(n,nam1)
归纳起来,定义派生类构造函数的一般形式为: 派生类构造函数名(总参数表列): 基类构造函数名(参数表列),子对象名(参数表列) 执行派生类构造函数的顺序是: 派生类构造函数的总参数表列中的参数,应当包括基类构造函数和子对象的参数表列中的参数。基类构造函数和子对象的次序可以是任意的,如上面的派生类构造函数首部可以写成 Student1(int n,string ad): monitor(n1,nam1),Student(n,nam)
如果有多个子对象,派生类构造函数的写法依此类推,应列出每一个子对象名及其参数表列。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |