继承与派生
构造函数
在C++中,当创建一个派生类对象时,构造函数的调用顺序遵循以下规则:
- 基类的构造函数:首先调用基类的构造函数。如果有多个基类,将按照它们在派生类中声明的顺序来调用。
- 成员对象的构造函数:接下来,会调用派生类中成员对象的构造函数。这些成员对象的构造按照它们在类定义中声明的顺序进行。
- 派生类自身的构造函数:最后,调用派生类自身的构造函数。
所以,成员对象的构造函数会在派生类构造函数之前被调用。这是为了确保在派生类构造函数体执行前,所有基类和成员对象都已经正确初始化。这样的设计有助于维持对象的完整性,确保在使用派生类的任何功能之前,所有依赖的部分都已准备就绪。
析构函数
对于析构函数的调用顺序,它是构造函数调用顺序的逆过程:
- 派生类自身的析构函数:当派生类对象生命周期结束时,首先调用的是派生类自身的析构函数。
- 成员对象的析构函数:紧接着,按照它们在类中声明的逆序调用派生类中成员对象的析构函数。这意味着后声明的成员对象的析构函数会先被调用。
- 基类的析构函数:最后,按照基类在派生列表中声明的逆序调用基类的析构函数。如果有多个基类,最右边基类的析构函数最先被调用,依此类推。
总结起来,析构函数的调用顺序确保了资源能够按照正向构造时的逆序被正确释放,符合资源管理的最佳实践,避免了潜在的资源泄露问题。
继承方式
对于公有继承方式
(1) 基类成员对其对象的可见性:
公有成员可见,其他不可见。这里保护成员同于私有成员。
(2) 基类成员对派生类的可见性:
公有成员和保护成员可见,而私有成员不可见。这里保护成员同于公有成员。
(3) 基类成员对派生类对象的可见性:
公有成员可见,其他成员不可见。
所以,在公有继承时,派生类的对象可以访问基类中的公有成员;派生类的成员函数可以访问基类中的公有成员和保护成员。这里,一定要区分清楚派生类的对象和派生类中的成员函数对基类的访问是不同的。
对于私有继承方式
(1) 基类成员对其对象的可见性:
公有成员可见,其他成员不可见。
(2) 基类成员对派生类的可见性:
公有成员和保护成员是可见的,而私有成员是不可见的。
(3) 基类成员对派生类对象的可见性:
所有成员都是不可见的。
所以,在私有继承时,基类的成员只能由直接派生类访问,而无法再往下继承。
对于保护继承方式
这种继承方式与私有继承方式的情况相同。两者的区别仅在于对派生类的成员而言,对基类成员有不同的可见性。
上述所说的可见性也就是可访问性。关于可访问性还有另的一种说法。这种规则中,称派生类的对象对基类访问为水平访问,称派生类的派生类对基类的访问为垂直访问。
一般规则
公有继承时,水平访问和垂直访问对基类中的公有成员不受限制;
私有继承时,水平访问和垂直访问对基类中的公有成员也不能访问;
保护继承时,对于垂直访问同于公有继承,对于水平访问同于私有继承。
对于基类中的私有成员,只能被基类中的成员函数和友元函数所访问,不能被其他的函数访问。
基类与派生类的关系
任何一个类都可以派生出一个新类,派生类也可以再派生出新类,因此,基类和派生类是相对而言的。
总结
继承方式影响的是派生类对象,把派生类直接当成没继承时基类的对象去访问
子对象
类中以类作为私有数据成员,不能直接调用,需要使用成员函数调用
#include
#include
using namespace std;
//教师类
class Teacher
{
public:
Teacher(int,char [],char);
void display();
private:
int num;//工号
char name[20];//姓名
char sex;//性别 , m表示男,f表示女
};
Teacher::Teacher(int n,char nam[],char s)
{
num=n;
strcpy(name,nam);
sex=s;
}
void Teacher::display()
{
cout<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl;
cout<<"sex:"<<sex<<endl;
}
//日期类
class Date
{
public:
Date(int,int,int);
void display();
void change(int,int,int);
private:
int year;//年
int month;//月
int day;//日
};
Date::Date(int y,int m,int d)
{
year=y;
month=m;
day=d;
}
void Date::display()
{
cout<<"date:"<<month<<"/"<<day<<"/"<<year<<endl;
}
//修改日期
void Date::change(int y,int m,int d)
{
year=y;
month=m;
day=d;
}
//教授类
class Professor:public Teacher
{
public:
Professor(int n,char nam[20],char s,int y,int m,int d,char r[]);
void display();
void change(int,int,int);
private:
char research[50];//研究方向
Date birthday; //定义Date类的对象作为数据成员,生日
};
//构造函数
Professor::Professor(int n,char nam[20],char s,int y,int m,int d,char r[]):
Teacher(n,nam,s),birthday(y,m,d)
{
strcpy(research,r);
}
void Professor::display()
{
Teacher::display();
birthday.display();
cout<<“research:”<<research<<endl;
}
//修改生日日期
void Professor::change(int y,int m,int d)
{
birthday.change(y,m,d);
}
// original data:
// num:120080
// name:Zhang
// sex:f
// date:10/1/1949
// research:natural language processing
int main()
{
//定义Professor对象prof1
Professor prof1(120080,"Zhang",'f',1949,10,1,"natural language processing");
cout<<"original data:"<<endl;
//调用prof1对象的display函数
prof1.display();
cout<<endl<<"new data:"<<endl;
//调用prof1对象的change函数
prof1.change(1950,6,1);
//调用prof1对象的display函数
prof1.display();
return 0;
}
//上面的程序中使用了继承和组合。请按要求完成任务:
//
//(1)根据程序已有代码,增加Professor类的实现代码。
//
//(2)不得更改main函数代码,不得更改Teachere的代码,不得更改Date类代码,不得更改Professor类已有的声明代码。
//
//(3)补充完成的程序具有如下的输入、输出数据格式。
//
// 输入数据格式:
//
// 无
//
// 输出数据格式:
//
// (提示:此处有一行空行)
// new data:
// num:120080
// name:Zhang
// sex:f
// date:6/1/1950
// research:natural language processing
错题
在声明派生类时,如果不显式地给出继承方式,默认的类继承方式是公有继承public。( )
- A. 对
- B. 错
我的答案: 对正确答案: 错