转于:http://c.biancheng.net/cpp/biancheng/view/231.html
用户在声明类时可以不定义构造函数,系统会自动设置一个默认的构造函数,在定义类对象时会自动调用这个默认的构造函数。这个构造函数实际上是一个空函数,不执行任何操作。如果需要对类中的数据成员初始化,应自己定义构造函数。
构造函数的主要作用是对数据成员初始化。基类的构造函数是不能继承的,在声明派生类时,派生类并没有把基类的构造函数继承过来,因此,对继承过来的基类成员初始化的工作也要由派生类的构造函数承担。所以在设计派生类的构造函数时,不仅要考虑派生类所增加的数据成员的初始化,还应当考虑基类的数据成员初始化。 也就是说,希望在执行派生类的构造函数时,使派生类的数据成员和基类的数据成员同时都被初始化。 解决这个问题的思路是,在执行派生类的构造函数时,调用基类的构造函数。
任何派生类都包含基类的成员,简单的派生类只有一个基类,而且只有一级派生(只有直接派生类,没有间接派生类),在派生类的数据成员中不包含基类的对象(即子对象)。
下面先介绍一下在简单派生类中如何定义构造函数。
[例11.5] 简单的派生类的构造函数。
- #include <iostream>
- #include<string>
- using namespace std;
- class Student//声明基类Student
- {
- public:
- Student(int n,string nam,char s) //基类构造函数
- {
- num=n;
- name=nam;
- sex=s;
- }
- ~Student( ){ } //基类析构函数
- protected : //保护部分
- int num;
- string name;
- char sex ;
- };
- class Student1: public Student //声明派生类Student1
- {
- public : //派生类的公用部分
- Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s)//派生类构造函数
- {
- age=a; //在函数体中只对派生类新增的数据成员初始化
- addr=ad;
- }
- void show( )
- {
- cout<<"num: "<<num<<endl;
- cout<<"name: "<<name<<endl;
- cout<<"sex: "<<sex<<endl;
- cout<<"age: "<<age<<endl;
- cout<<"address: "<<addr<<endl<<endl;
- }
- ~Student1( ){ } //派生类析构函数
- private : //派生类的私有部分
- int age;
- string addr;
- };
- int main( )
- {
- Student1 stud1(10010,"Wang-li",'f',19,"115 Beijing Road,Shanghai");
- Student1 stud2(10011,"Zhang-fun",'m',21,"213 Shanghai Road,Beijing");
- stud1.show( ); //输出第一个学生的数据
- stud2.show( ); //输出第二个学生的数据
- return 0;
- }
num:10010
name:Wang-li
sex:f
address: 115
Beijing Road,Shanghai
num:10011
name:Zhang-fun
sex:m
address: 213
Shanghai Road,Beijing
请注意派生类构造函数首行的写法:
Student1(int n, string nam, char s, int a, string ad):Student(n, nam, s)
其一般形式为:
派生类构造函数名(总参数表列): 基类构造函数名(参数表列) {派生类中新增数据成员初始化语句}
冒号前面的部分是派生类构造函数的主干,它和以前介绍过的构造函数的形式相同,但它的总参数表列中包括基类构造函数所需的参数和对派生类新增的数据成员初始化所需的参数;冒号后面的部分是要调用的基类构造函数及其参数。
从上面列出的派生类Student1构造函数首行中可以看到,派生类构造函数名(Student1)后面括号内的参数表列中包括参数的类型和参数名(如int n),而基类构造函数名后面括号内的参数表列只有参数名而不包括参数类型(如n、nam、s),因为在这里不是定义基类构造函数,而是调用基类构造函数,因此这些参数是实参而不是形参。它们可以是常量、全局变量和派生类构造函数总参数表中的参数。
从上面列出的派生类Student1构造函数中可以看到:调用基类构造函数Student时给出3个参数(n、nam、s),这是和定义基类构造函数时指定的参数相匹配的。派生类构造函数 student1 有5个参数,其中前3个是用来传递给基类构造函数的,后面两个(a和ad)是用来对派生类所增加的数据成员初始化的。
在main函数中,建立对象stud1时指定了5个实参。它们按顺序传递给派生类构造函数Student1的形参。然后,派生类构造函数将前面3个传递给基类构造函数的形参。
在上例中也可以将派生类构造函数在类外面定义,而在类体中只写该函数的声明:
Student1(int n, string nam, char s, int a, string ad);
在类的外面定义派生类构造函数:
- Student :: Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s)
- {
- age=a;
- addr=ad;
- }
在以上的例子中,调用基类构造函数时的实参是从派生类构造函数的总参数表中得到的,也可以不从派生类构造函数的总参数表中传递过来,而直接使用常量或全局变量。例如,派生类构造函数首行可以写成以下形式:
Student1(string nam,char s,int a,string ad):Student(10010,nam,s)
即基类构造函数3个实参中,有一个是常量10010,另外两个从派生类构造函数的总参数表传递过来。
上面介绍的派生类构造函数的定义形式,和普通类构造函数的初始化表(请查看: C++带参数的构造函数 )的形式很类似,请回顾一下普通类中构造函数初始化表的例子:
Box::Box(int h,int w,int len):height(h), width(w), length(len) { }
它也有一个冒号,在冒号后面的是对数据成员的初始化表。
实际上,这里介绍的在派生类构造函数中对基类成员初始化,就是普通类构造函数初始化表。 也就是说,不仅可以利用初始化表对构造函数的数据成员初始化,而且可以利用初始化表调用派生类的基类构造函数,实现对基类数据成员的初始化。也可以在同一个构造函数的定义中同时实现这两种功能。
例如,例11.5中派生类的基类构造函数的定义采用了下面的形式:
- Student1(int n, string nam,char s,int a, string ad):Student(n,nam,s)
- {
- age=a; //在函数体中对派生类数据成员初始化
- addr=ad;
- }
Student1(int n, string nam,char s,int a, string ad):Student(n,nam,s),age(a),addr(ad){}
这样函数体为空,更显得简单和方便。
在建立一个对象时,执行构造函数的顺序是:
- 派生类构造函数先调用基类构造函数;
- 再执行派生类构造函数本身(即派生类构造函数的函数体)。
对上例来说,先初始化num,name,sex,然后再初始化age和addr。
在派生类对象释放时,先执行派生类析构函数~Student1( ),再执行其基类析构函数~Student( )。