7.类的继承与派生
1.派生类生成过程
Step1
吸收继承基类的全部成员,除构造及析构函数
Step2
如果派生类声明了一个和基类同名的成员,那么派生类在调用时只会调用派生类中的成员,而非基类。
如果派生类声明了一个和基类同名的函数**(参数表也相同,否则是重载)**,那么派生类在调用时只会调用派生类中的函数,而非基类。
Step3
添加新的成员
2.访问控制
不管何种继承方式,基类的私有成员均不可在派生类中访问
1.公有继承
基类中的公有和保护成员依旧是公有和保护成员。
2.私有继承
基类中的公有和保护成员均成为派生类的私有成员。(使用较少)
3.保护继承
基类中的公有和保护成员均成为派生类的保护成员。
3.类型兼容规则
类型兼容规则是指任何在使用基类对象的地方都可以用派生类对象代替
具体内容
class B{};
class D:public class B{};
B b1,*pb1;
D d1;
1.派生类对象可以隐含转换为基类对象
b1=d1
将d1中的各继承成员赋值给b1
2.派生类对象可以初始化基类对象的引用
B &rb=d1
3.派生类对象的地址可以隐含转换为指向基类的指针
pb1=&d1
EG:
#include <iostream>
using namespace std;
class A
{
public:
void show(){cout<<"A is playing"<<endl;}
};
class B:public A
{
public:
void show(){cout<<"B is playing"<<endl;}
};
int main()
{
A a1,*pa;
B b1;
pa=&b1; //派生类地址隐含转换为指向基类的指针,只能访问从基类继承的成员
pa->show();
}
输出结果
A is playing
4.派生类的构造和析构函数
1.构造函数
在构造派生类对象时,要对基类的成员对象和派生类新增的成员对象一起进行初始化,需要调用基类的构造函数。
如果对基类初始化时,需要调用基类带有形参表的构造函数,就需要为派生类新增构造函数
派生类构造函数的语法形式
派生类名::派生类名:基类1(形参),基类2(形参),成员1(形参),成员2(形参){}
派生类构造函数的执行次序
1.按照被继承时的声明顺序调用基类的构造函数
2.按照在类中声明的顺序对新增成员进行初始化
3.执行函数体内容
(若有虚基类,则虚基类对象最先被构造)
EG 7-4
#include <iostream>
using namespace std;
class Base1
{
public:
Base1(int i){cout<<"constructing Base1 with "<<i<<endl;}
};
class Base2
{
public:
Base2(int j){cout<<"constructing Base2 with "<<j<<endl;}
};
class Base3
{
public:
Base3(){cout<<"constructing Base3~~~"<<endl;}
};
class Derived:public Base2,public Base1,public Base3
{
public:
Derived(int a,int b,int c,int d):Base1(a),Base2(b),mem1(c),mem2(d){} //Base3构造函数无需形参,故不用在这里声明,会自动按照顺序构造
private:
Base2 mem2;
Base3 mem3;
Base1 mem1;
};
int main()
{
Derived d0(1,2,3,4);
return 0;
}
输出结果
constructing Base2 with 2
constructing Base1 with 1
constructing Base3~~~
constructing Base2 with 4
constructing Base3~~~
constructing Base1 with 3
2.复制构造函数
假定Derived类是Base类的派生类,可以这样写Derived类的复制构造函数:
Derived::Derived(const &v):Base(v){}
1.使用常引用是避免v的成员被改变,一般的复制构造函数都可以使用常引用
2.根据类型兼容规则,可以用Derived的对象V去初始化Base类
3.析构函数
派生类的析构函数与一般无继承关系的类的析构函数完全相同,只需要对新增成员进行清理,系统会自动调用基类的析构函数清理基类的成员
派生类析构函数的执行次序与构造函数正好完全相反
所谓析构函数,会在程序结束or调用时发生作用
5.派生类成员的标识与访问
1.作用域标识符
派生类中若有与基类同名的成员或函数,则会隐藏基类中的成员或函数。若派生类的多个基类均有同名成员或函数,则会隐藏所有。
这时若要访问基类的成员,就需要用作用域标识符**“::”**
·若派生类有多个基类,这些基类有同名成员,而派生类未声明同名成员,就必须用作用域标识符,或者using语句表明调用哪个基类的成员
EG
class Derived:public Base1,public Base2
{
public:
using Base1::var;
using Base2::showfun();
};
·若基类与派生类有同名函数,但形参表不同,则在派生类中使用using语句,可以使两个同名函数并存,成为重载形式
EG
class Derived2:public Base1
{
public:
using Base1::showfun();
void showfun(int i);
}
·若派生类的多个基类是从同一基类派生而来,则会有同名成员,需要运用标识符
2.虚基类
若派生类的多个基类是从同一基类派生而来,则可以将这个共同的基类设置为虚基类,这样便不需要再用作用域标识符去限定选取哪个基类的成员。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tBC0Rz4j-1668239167370)(…/…/…/…/users/mickey/typora/%E6%88%AA%E5%B1%8F2022-11-12%2015.38.40.png)]
语法形式
class Base1:virtual public Base0{};
所有虚基类的直接或间接派生类的构造函数中必须对该虚基类进行初始化(若虚基类没有带形参表的构造函数,则无需这么做)