用户在声明类时可以不定义构造函数,系统会自动设置一个默认的构造函数,在定义类对象时会自动调用这个默认的构造函数。这个构造函数实际上是一个空函数,不执行任何操作。如果需要对类中的数据成员初始化,应自己定义构造函数。
构造函数的主要作用是对数据成员初始化。在设计派生类的构造函数时,不仅要考虑派生类所增加的数据成员的初始化,还应当考虑基类的数据成员初始化。也就是说,希望在执行派生类的构造函数时,使派生类的数据成员和基类的数据成员同时都被初始化。 解决这个问题的思路是: 在执行派生类的构造函数时,调用基类的构造函数。
任何派生类都包含基类的成员,简单的派生类只有一个基类,而且只有一级派生(只有直接派生类,没有间接派生类),在派生类的数据成员中不包含基类的对象(即子对象)。
例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)
其一般形式为
派生类构造函数名(总参数表列):基类构造函数名(参数表列){派生类中新增数据成员初始化语句}
在main函数中,建立对象stud1时指定了5个实参。它们按顺序传递给派生类构造函数Student1的形参。然后,派生类构造函数将前面3个传递给基类构造函数的形参。
在上例中也可以将派生类构造函数在类外面定义,而在类体中只写该函数的声明:
Student1(int n, string nam, char s, int a, string ad);
在类的外面定义派生类构造函数:
Student1∷Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s){age=a;addr=ad; }
请注意: 在类中对派生类构造函数作声明时,不包括基类构造函数名及其参数表列(即Student(n, nam, s))。 只在定义函数时才将它列出。
在以上的例子中,调用基类构造函数时的实参是从派生类构造函数的总参数表中得到的,也可以不从派生类构造函数的总参数表中传递过来,而直接使用常量或全局变量。例如,派生类构造函数首行可以写成以下形式:
Student1(string nam,char s,int a,string ad):Student(10010,nam,s)
即基类构造函数3个实参中,有一个是常量10010,另外两个从派生类构造函数的总参数表传递过来。
请回顾一下在前面介绍过的构造函数初始化表的例子:
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;
}
可以将对age和addr的初始化也用初始化表处理,将构造函数改写为以下形式: 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( )。
构造函数的主要作用是对数据成员初始化。在设计派生类的构造函数时,不仅要考虑派生类所增加的数据成员的初始化,还应当考虑基类的数据成员初始化。也就是说,希望在执行派生类的构造函数时,使派生类的数据成员和基类的数据成员同时都被初始化。 解决这个问题的思路是: 在执行派生类的构造函数时,调用基类的构造函数。
任何派生类都包含基类的成员,简单的派生类只有一个基类,而且只有一级派生(只有直接派生类,没有间接派生类),在派生类的数据成员中不包含基类的对象(即子对象)。
例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)
其一般形式为
派生类构造函数名(总参数表列):基类构造函数名(参数表列){派生类中新增数据成员初始化语句}
在main函数中,建立对象stud1时指定了5个实参。它们按顺序传递给派生类构造函数Student1的形参。然后,派生类构造函数将前面3个传递给基类构造函数的形参。
在上例中也可以将派生类构造函数在类外面定义,而在类体中只写该函数的声明:
Student1(int n, string nam, char s, int a, string ad);
在类的外面定义派生类构造函数:
Student1∷Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s){age=a;addr=ad; }
请注意: 在类中对派生类构造函数作声明时,不包括基类构造函数名及其参数表列(即Student(n, nam, s))。 只在定义函数时才将它列出。
在以上的例子中,调用基类构造函数时的实参是从派生类构造函数的总参数表中得到的,也可以不从派生类构造函数的总参数表中传递过来,而直接使用常量或全局变量。例如,派生类构造函数首行可以写成以下形式:
Student1(string nam,char s,int a,string ad):Student(10010,nam,s)
即基类构造函数3个实参中,有一个是常量10010,另外两个从派生类构造函数的总参数表传递过来。
请回顾一下在前面介绍过的构造函数初始化表的例子:
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;
}
可以将对age和addr的初始化也用初始化表处理,将构造函数改写为以下形式: 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( )。