C++_16_继承和派生 - 重写版
C++继承和派生
继承和派生的概念:
继承和派生 是 “类” 和 “类” 之间的关系,描述的是从一个类到另一个类的过程,继承和派生是描述的同一个过程,只不过看待的角度不同
先出现的类是父类或基类,后生成的类是子类
继承:子类到父类叫做继承
派生:父类到子类叫做派生
由于派生类可以将基类的代码继承过来,无需重新设计,因而继承解决了代码重用的问题,大大提高了软件的开发效率。同时如果这些代码在基类中运行无误,则继承之后到派生类中运行也是不会有问题的。
派生和继承可以叠加嵌套,即A类派生出B类,B类也可以派生出C类
//前面学的一大堆都是封装
//现在开始学习第二板块--继承和派生
#include<iostream>
using namespace std;
class html //父类
{
public:
void headhtml(void)
{
cout << "网页头部" << endl;
}
void lefthtml(void)
{
cout << "左侧栏" << endl;
}
void mainhtml(void)
{
cout << "网页主题" << endl;
}
void endhtml(void)
{
cout << "网页底部" << endl;
}
private:
};
class ithtml :public html //子类ithtml公有制继承于父类html
{
public:
//除了继承出来的完全一样的成员,还可以尽快客制化自己的成员
void selfhtml(void)
{
cout << "个人网页" << endl;
}
};
int main()
{
ithtml it;
it.headhtml(); //继承而来的成员函数
it.mainhtml();
it.lefthtml();
it.endhtml();
it.selfhtml(); //客制化自己的成员函数
return 0;
}
三种类继承方式:
1、public继承
2、protected继承
3、private继承
public 成员可以通过对象来访问,private 成员不能通过对象访问。
protected 成员和 private 成员类似,也不能通过对象访问。但是当存在继承关系时,protected 和 private 就不一样了:基类中的 protected 成员可以在派生类中使用,而基类中的 private 成员不能在派生类中使用
实际上:
1、子类继承父类, 子类拥有父类中(除了构造和析构之外)全部成员变量和成员方法;包括公有、保护、私有成员变量和函数,但是不包含构造和析构,因为这个独立类特有的,不在可继承范围内。
2、基类的 哪怕是 private 成员是能够被继承的,并且(成员变量)会占用派生类对象的内存,它只是在派生类中不可见,无法访问罢了。
3、在派生类中访问基类 private 成员的唯一方法就是借助基类的非 private 成员函数,如果基类没有非 private 成员函数,那么该成员在派生类中将无法访问。
继承方式/基类成员 | public成员 | protected成员 | private成员 |
---|---|---|---|
public继承 | public | protected | 不可见 |
protected继承 | protected | protected | 不可见 |
private继承 | private | private | 不可见 |
被继承的类看做是原材料,继承方式看做是加工条件;条件从苛刻到放松依次为:private继承 > protected继承 > public继承
public继承的条件最放松限制最小,private继承条件最苛刻限制最大;
单继承:指每个派生类只直接继承了一个基类的特征 (一个父类 派生出 一个子类)
多继承:指多个基类派生出一个派生类的继承关系,多继承的派生类直接继承了不止一个基类的特征(多个父类 派生出 一个子类)
继承的内层结构:
Developer Command Prompt for VS 2022
内层结构:cl d1 reportSingleClassLayout(继承的类名) test.cpp
更改访问权限:
使用 using 关键字可以改变基类成员在派生类中的访问权限,但是using 只能改变基类中 public 和 protected 成员的访问权限,不能改变 private 成员的访问权限,因为基类中 private 成员在派生类中是不可见的,根本不能使用,所以基类中的 private 成员在派生类中无论如何都不能访问。
class People {
public:
void show();
protected:
char *m_name;
int m_age;
};
//派生类Student
class Student : public People {
public:
void learning();
public:
using People::m_name; //将protected改为public
using People::m_age; //将protected改为public
float m_score;
private:
using People::show; //将public改为private
};
C++成员变量(成员函数)名的覆盖(遮蔽):
如果派生类中新增一个成员变量(或成员函数),该成员变量(或成员函数)与基类中的成员变量(或成员函数)同名,则新增的成员变量(或成员函数)就会遮蔽从基类中继承过来的成员变量(或成员函数)。
被遮蔽了的基类的成员变量或成员函数并非是没有继承过来,而仅仅是被派生类的同名成员变量和成员函数给遮蔽了,调用的时候需要用到类名加上域解析操作符((被遮蔽的)类名::)。
C++间接继承:(继承具有传递定,亲缘关系)
继承可以是直接继承,也可以是间接继承!
假设C类继承B类,B类继承A类,相互联系的直接继承;C类还可以直接继承A类,跳过B类,变为间接继承。
虽然可以理解间接继承,但间接继承所得的成员变量和成员函数,其属性遵循直接继承时的规则,亲缘关系的问题
C++继承机制下的构造函数———构造函数也能被继承,但派生类如果变异则需要先先执行基类构造函数,然后再去执行派生类构造函数
派生类同样有构造函数,当派生出新的类后,创建的派生类的对象时,同样会调用构造函数进行初始化,用于初始化派生类从基类中继承过来的成员变量。而派生类中新增的成员变量则需要重新定义构造函数用于初始化了。
创建派生类对象时,先由派生类构造函数调用基类构造函数,然后再执行派生类构造函数函数体中的内容,也就是说先执行基类构造函数,然后再去执行派生类构造函数。
创建对象时先是执行基类的构造函数,然后再是执行派生类的构造函数。构造函数的调用顺序是按照继承的层次,自顶向下,从基类再到派生类的。
C++派生类构造函数调用规则
派生类构造函数可以自动调用基类的默认构造函数,但是前提是默认构造函数必须存在。
但如果我们在基类中定义了一个带参数的构造函数,那么基类就不会在生成默认构造函数,基类没有默认构造函数怎么办?|
解决方案:
1、在基类中定义一个默认构造函数(不带参数的构造函数) 源头处解决,从基类解决,源头止水,一劳永逸
2、派生类每个构造函数都显式的调用(手动调用)基类中的带参构造函数(自己写的) 末节处解决,从派生类解决,亡羊补牢
在创建派生类对象时,必须显式或隐式地调用基类的某一个构造函数,这一点非常重要。当然被调用的基类的构造函数可以是带参构造函数,也可以是默认构造函数。
C++继承机制下的析构函数
创建派生类对象时,构造函数的调用顺序是按照继承顺序,先执行基类构造函数,然后再执行派生类的构造函数。但是对于析构函数,其调用顺序是正好相反的,即先执行派生类的构造函数,然后再执行基类的构造函数。
每一个类中最多只能有一个析构函数,因此调用的时候并不会出现二义性,因此析构函数不需要显式的调用。
继承中的构造和析构顺序
构造顺序: 父类(基类)构造 ------> 子类(派生类)构造
析构顺序:子类(派生类)析构------> 父类 (基类) 析构
子类中 有父类、对象成员 构造和析构的顺序
父类和子类的同名 成员变量 处理
1、当 父类和子类 成员变量同名时 在子类就近原则 选择本作用域的子类成员
2、如果在子类中 必须使用父类中的同名成员 必须加上父类的作用域。
3、子类可以借助 父类的公有方法 间接的操作 父类的私有数据(不可见的数据)
父类和子类的同名 成员函数 处理
在子类继承了父类的成员函数后,父类和子类成员函数同名,使用子类定义一个对象;
如果子类自己再将同名成员重新进行定义,那么将使用子类的成员函数,父类同名成员函数将会被屏蔽;
否者,如果子类没有再将同名成员函数进行定义,则会默认使用的仍旧是父类的成员函数;