派生类构造函数一般形式为:
派生类构造函数名(总参数表):基类构造函数名(参数表)
{派生类中新增数据成员初始化语句}
例:定义简单的派生类的构造函数
#include
#include
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;
}
程序分析:
- 从上面列出的派生类Student1构造函数首行中可以看到,派生类构造函数名(Student1)后面括号内的参数表中包含参数的类型和参数名(如:int n),而基类构造函数名后面括号内的参数表列只有参数名而不包括参数类型(如:n,nam,s),因为在这里不是定义基类构造函数,而是调用基类构造函数,因此这些参数是实参而不是形参。它们可以是常量、全局变量和派生类构造函数总参数表中的参数。
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(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) {}
- 在建立一个对象时,执行构造函数的顺序是:①派生类构造函数先调用基类构造函数;②再执行派生类构造函数本身(即派生类构造函数的函数体)。
- 在派生类对象释放时,先执行派生类析构函数~Student1(),再执行其基类析构函数~Student。
运行结果:
Student s1; //Student是已声明的类名,s1是Student类的对象
例:包含子对象的派生类的构造函数
#include
#include
using namespace std;
class Student //声明基类
{
public: //公用部分
Student(int n, string nam) //基类构造函数
{
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 << "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 BeijingRoad, Shanghai");
stud1.show(); //输出学生的数据
stud1.show_monitor(); //输出子对象的数据
return 0;
}
程序分析:
- 派生类Student1中有一个数据成员:
Student monitor; //定义子对象monitor(班长)
,"班长"的类型不是简单类型,它是Student类的对象。应当在建立对象时对它的数据成员初始化,显然不能在声明派生类时对它初始化(如:Student monitor(10001,"Li_sun");
),因为类是抽象类型,只是一个模型,是不能有具体的数据的,而且每一个派生类对象的子对象一般是不相同的。因此子对象的初始化是在建立派生类时通过调用派生类构造函数来实现的。- 程序中派生类构造函数首部是:
Student1(int n, string nam, int n1, string nam1, int a, string ad) : Student(n, nam), monitor(n1, nam1)
。
运行结果:
派生类构造函数名(总参数表):基类构造函数名(参数表),子对象名(参数表)
{派生类中新增数据成员初始化语句}
Student1(int n, string nam, int n1, string nam1, int a, string ad) :monitor(n1, nam1), Student(n, nam)
。编译系统是根据相同的参数名(而不是根据参数的顺序)来确立它们的传递关系的。例:多级派生情况下派生类的构造函数
#include
#include
using namespace std;
class Student //声明基类
{
public: //公用部分
Student(int n, string nam) //基类构造函数
{
num = n;
name = nam;
}
void display() //成员函数,输出基类数据成员
{
cout << "num:" << num << endl;
cout << "name:" << name << endl;
}
protected: //保护部分
int num; //基类有两个数据成员
string name;
};
class Student1:public Student //声明公用派生类Student1
{
public:
Student1(int n, string nam, int a) :Student(n, nam) //派生类构造函数
{
age = a; //在此处只对派生类新增的数据成员初始化
}
void show() //输出num,name和age
{
display(); //输出num和name
cout << "age:" << age << endl; //输出age
}
private: //派生类的私有数据
int age; //增加一个数据成员
};
class Student2:public Student1 //声明间接派生类Student2
{
public: //下面是间接派生类构造函数
Student2(int n,string nam,int a,int s):Student1(n,nam,a)
{
score = s;
}
void show_all() //输出全部数据成员
{
show(); //输出num,name和age
cout << "score:" << score << endl; //输出score
}
private:
int score; //增加一个数据成员
};
int main()
{
Student2 stud(10010, "Li", 17, 89);
stud.show_all(); //输出学生的全部数据
return 0;
}
程序分析:
注意基类和两个派生类的构造函数的写法
- 基类的构造函数首部:
Student(int n, string nam)
- 派生类Student1的构造函数首部:
Student1(int n, string nam, int a) :Student(n, nam)
- 派生类Student1的构造函数首部:
Student2(int n,string nam,int a,int s):Student1(n,nam,a)
。
注意:不要写成Student2(int n,string nam,int a,int s):Student(n, nam),Student1(n,nam,a)
。不要列出每一层派生类的构造函数,只须写出其上一层派生类(即它的直接基类)的构造函数即可。
运行结果:
在使用派生类构造函数时,可以有以下两种特殊的形式:
Student1(int n,string nam,int n1,string nam1):Student(n,nam),monitor(n1,nam1){ }
class D:public A,private B,protected C
{类D新增加的成员}
D是多重继承的派生类,它以公用继承方式继承A类,以私有继承方式继承B类,以保护继承方式继承C类。D按不同的继承方式的规则继承A、B、C的属性,确定各基类的成员在派生类中的访问权限。
派生类构造函数名(总参数表列):基类1构造函数(参数表列),基类2构造函数(参数表列),基类3构造函数(参数表列)
{
派生类中新增数成员据成员初始化语句
}
各基类的排列顺序任意。派生类构造函数的执行顺序同样为先调用基类的构造函数,再执行派生类构造函数的函数体。调用基类构造函数的顺序是按照声明派生类时基类出现的顺序。
例:声明一个教师(Teacher)类和一个学生(Student)类,用多重继承的方式声明一个在职研究生(Graduate)派生类(在职教师攻读研究生)。
#include
#include
using namespace std;
class Teacher //声明类Teacher(教师)类
{
public: //公用部分
Teacher(string nam, int a, string t) //构造函数
{
name = nam;
age = a;
title = t;
}
void display()
{
cout << "name:" << name << endl;
cout << "age:" << age << endl;
cout << "title:" << title << endl;
}
protected: //保护部分
string name;
int age;
string title; //职称
};
class Student //定义类Student(学生)
{
public:
Student(string nam, char s, float sco) //构造函数
{
name1 = nam;
sex = s;
score = sco;
}
void display1()
{
cout << "name:" << name1 << endl;
cout << "sex:" << sex << endl;
cout << "score:" << score << endl;
}
protected: //保护部分
string name1;
char sex;
float score; //成绩
};
class Graduate :public Teacher, public Student
{
public:
Graduate(string nam,int a,char s,string t,float sco,float w):Teacher(nam,a,t),Student(nam,s,sco),wage(w){}
void show() //输出研究生的有关数据
{
cout << "name:" << name1 << endl;
cout << "age:" << age << endl;
cout << "sex:" << sex << endl;
cout << "score:" << score << endl;
cout << "title:" << title << endl;
cout << "wages:" << wage << endl;
}
private:
float wage; //津贴
};
int main()
{
Graduate grad1("Wang_li", 24, 'f', "assistance", 89.5, 1200);
grad1.show();
return 0;
}
程序分析:
- 由于在两个基类中把数据成员声明为protected,因此可以通过派生类的成员函数引用基类的成员。如果在基类中把数据成员声明为private,则派生类成员函数不能引用这些数据。
- 在两个基类中分别用name和name1来代表姓名,其实这是同一个人的名字,从Graduate类的构造函数中可以看到总参数表中的参数nam分别传递给两个基类的构造函数,作为基类构造函数的实参。现在两个基类都需要有姓名这一项,能否用同一个名字name来代表?——在本程序中只作这样的修改是不行的,因为在同一个派生类中存在着两个同名的数据成员,在派生类的成员函数show中引用name时就会出现二义性,编译系统无法判定应该选择哪一个基类中的name。解决这个问题有一个好方法:在两个基类中可以都使用同一个数据成员名name,而在show函数中引用数据成员时指明其作用域,如:
cout<<"name:”<
,这就是唯一的,不致引起二义性,能通过编译,正常运行。
运行结果:
类A和类B中都有成员函数display和数据成员a,类C是类A和类B的直接派生类。分别讨论下列3种情况:
class A
{
public:
int a;
void display();
};
class B
{
public:
int a;
void display();
};
class C :public A, public B //公用继承
{
public:
int b;
void show();
};
程序分析:
- 如果在main函数中定义C类对象c1,并调用数据成员a和成员函数display:
C c1; //定义C类对象c1
c1.a = 3; //引用c1的数据成员a
c1.display(); //调用c1的成员函数display
- 由于基类A和基类B都有数据成员a和成员函数display,编译系统无法判定要访问的是哪一个基类的成员,因此,程序编译出错。解决此问题,可以用基类名来限定:
c1.A::a = 3; //引用c1对象中的基类A的数据成员a
c1.A::display(); //调用c1对象中的基类A的成员函数display
- 如果派生类C中的成员函数show访问基类A的display和a,可以不必写对象名而直接写
A::a = 3; //指当前对象
A::display();
class A
{
public:
int a;
void display();
};
class B
{
public:
int a;
void display();
};
class C :public A, public B //公用继承
{
int a;
void display();
};
程序分析:
- 如果在main函数中定义C类对象c1,并调用数据成员a和成员函数display:
C c1;
c1.a = 3;
c1.display();
c1.A::a = 3; //表示派生类对象c1中的基类A中的数据成员a
c1.A::display(); //表示派生类对象c1中的基类A中的成员函数display
3.如果类A和类B是从同一个基类派生的(如图5)
class N
{
public:
int a;
void display() { cout << "A::a=" << a << endl; }
};
class A :public N
{
public:
int a1;
};
class B :public N
{
public:
int a2;
};
class C :public A, public B
{
public:
int a3;
void show() { cout << "a3=" << a3 << endl; }
};
int main()
{
C c1; //定义C类对象c1
...
}
程序分析:
- 在类A和类B中虽然没有定义数据成员a和成员函数display,但是它们分别从类N继承了数据成员a和成员函数display,这样在类A和类B中同时存在着两个同名的数据成员a和成员函数display。它们是类N成员的拷贝。类A和类B中的数据成员a代表两个不同的存储单元,可以分别存放不同的数据。在程序中可以通过类A和类B的构造函数去调用基类N的构造函数,分别对类A和类B的数据成员a初始化。
- 访问类A中基类N继承下来的成员,显然不能用
c1.a = 3; c1.display();
或c1.N::a = 3; c1.N::display();
。因为这样依然无法区别是类A中从基类N继承下来的成员,还是类B中从基类N继承下来的成员。应当通过类N的直接派生类名来指出要访问的是类N的哪一个派生类中的基类成员。如c1.A::a = 3; c1.A::display(); //要访问的是类N的派生类A中的基类成员
派生类C中成员的情况: