C++基础3:继承
1、语法
(1)原则
is-a
父类/子类
基类/派生类
(2)语法
class 派生类 : [访问限定符] 基类 {
成员
}
如果不写继承访问限定符,默认是private
2、成员的访问权限
public | protected | private | |
---|---|---|---|
类成员函数 | √ | √ | × |
友元函数 | √ | √ | √ |
子类函数 | √ | √ | × |
类对象 | √ | × | × |
子类继承了父类所有的成员变量和成员函数。与访问限定符无关。访问限定符只是限制了访问
子类访问父类成员变量,把父类成员变量访问限定符,改为protected。
(1)继承访问权限变化
分为子类内部和子类对象两种访问方式
1、子类内部访问public继承的父类成员变量
#include <iostream>
using namespace std;
class Base{
public:
int public_data;
protected:
int protected_data;
private:
int private_data;
};
class Derive:public Base{
public:
void test(){
cout << public_data <<endl;
cout << protected_data << endl;
cout << private_data << endl;
}
};
这个时候会提示编译出错,不能成功,因为子类Derive继承父类Base的public,protected但是不能访问父类的private。
2、子类内部访问public继承的父类成员函数
#include <iostream>
using namespace std;
class Base{
public:
int public_data;
void public_func();
protected:
int protected_data;
void protected_func();
private:
int private_data;
void private_func();
};
class Derive:public Base{
public:
void test(){/*
cout << public_data <<endl;
cout << protected_data << endl;
cout << private_data << endl;*/
public_func();
protected_func();
private_func();
}
};
这个时候也会出现同样的错误,因为子类Derive是public的,所以不能访问父类中private的函数
(2)子类内部访问父类成员
public | protected | private | |
---|---|---|---|
public继承 | √ | √ | × |
protected继承 | √ | √ | × |
private继承 | √ | √ | × |
子类内部访问父类成员,只能访问public和protected成员
(3)子类对象访问父类成员
public | protected | private | |
---|---|---|---|
public继承 | √ | × | × |
protected继承 | × | × | × |
private继承 | × | × | × |
子类只有public继承父类的时候,才能访问父类的public成员,其他都不能访问,通常子类使用public继承子类
(4)子类对象访问父类成员访问限定符的变化
继承方式/父类成员 | public | protected | private |
---|---|---|---|
public继承 | public | protected | 不可见 |
protected继承 | protected | protected | 不可见 |
private继承 | private | private | 不可见 |
(5)小结
1、public无论类内部还是类对象都可以访问
2、protected类对象不可访问,类内部与继承类的内部都可以访问
3、private只有类内部才可以访问
3、继承关系的构造顺序
(1)顺序
1、派生类的构造函数与析构函数的调用顺序
2、派生类的构造函数调用顺序
子对象构造、成员变量构造、父对象构造的顺序
3、派生类的析构函数调用顺序
子对象析构、成员变量析构、父对象析构的顺序
#include <iostream>
using std::cout;
using std::endl;
class Member{
public:
Member(){
cout << "Member Init" <<endl;
}
~Member(){
cout << "Member Destroy" <<endl;
}
};
class Parent{
public:
Parent(){
cout << "Parent Init" <<endl;
}
~Parent(){
cout << "Parent Destory" <<endl;
}
};
class Son : public Parent{
public:
Son(){
cout << "Son Init" <<endl;
}
~Son(){
cout << "Son Destroy" <<endl;
}
private:
Member m;
};
int main(){
Son son;
}
运行结果:
Parent Init
Member Init
Son Init
Son Destory
Member Destory
Parent Destory
这样的输出结果是因为,Son需要先初始化自己的父类,然后自己的成员,最后是自己的类,然后开始析构,,从自己开始析构,然后是成员,最后是父类的析构。
没有默认构造函数的基类在派生类的初始化,必须在初始化列表中初始化
(2)同名隐藏规则
概念:子类的成员函数与基类成员函数同名,子类的函数将会隐藏基类的所有同名函数
#include <iostream>
using namespace std;
class Base{
public:
void show(int data){
cout << "Base::show(" << data << ")"<<endl;
}
};
class Derive:public Base{
public:
void show(){
cout << "Derive::show()" <<endl;
}
};
int main(){
Derive derive;
derive.show();
derive.show(123);
}
这时候编译出错,因为derive.show()调用的时候,父类中也有同名的函数,所以要想办法声明这个调用的show()是父类中的show()
修改成指针方式,试一下
int main(){
Derive *pderive = new Derive;
pderive->show();
pderive->show(123);
}
这个时候还是不行,所以需要想另外的办法来解决问题!
解决办法:
1、Derive类对象调用被隐藏的父类函数时,在函数前面加上父类限定符。例如:
derive.show(123)-->derive.Base::show(123)
pderive->show(123)-->pderive->Base::show(123)
2、Derive类中的名称会隐藏Base类中同名的名称,在public继承中我们可以通过using声明:
class Derive:public Base {
public:
using Base::show;
void show(){
cout<<"Derive::show()"<<endl;
}
};
隐藏背后原因是为防止在程序库或应用框架内建立新的derived class时从疏远的base class继承重载函数
4、函数同名的情况总结
名称 | 英语 |
---|---|
重载 | overload |
重写(覆盖) | override |
隐藏 | hide |
赋值兼容规则
概念:在任何需要基类对象的地方都可以使用公有的派生类对象来代替。反之,不可。
1、派生类的对象可以赋值给基类对象
Base base;
Derive derive;
base = derive;
对象切割:在赋值时舍弃派生类自己的成员,只进行基类数据成员的赋值
2、派生类的对象可以初始化基类的引用
Derive derive;
Base& base = derive;
3、派生类对象的地址可以赋值给指向基类的指针
指向基类对象的指针变量也可以指向派生类对象
Derive derive;
Base* base = &derive;
向上转换:派生类对象赋值给基类
向下转换:基类对象赋值给派生类
4.1 练习
(1)派生类的对象可以赋值给基类的对象
int main(){
Base base;
Derive derive;
base = derive;
base.show(123);
}
(2)派生类对象的地址赋值给基类的指针变量
int main(){
Base *pbase = new Derive;
pbase->show(123);
}
指针访问派生类中由基类继承来的对象,不能访问派生类中的新成员
(3)派生类对象可以初始化基类的引用
int main(){
Derive derive;
Base &base = derive;
base.show(123);
}
引用访问派生类中由基类继承来的对象,不能访问派生类中的新成员
5、多重继承
一个类可以同时继承多个父类的行为和特征功能。
逗号分割的基类列表
class 类名 : public 基类1,public 基类2{
};
6、钻石继承/菱形继承
6.1 概念
两个子类继承同一个父类,而又有子类同时继承这两个子类
6.2 问题1
下面B、C继承与A,D同时继承B、C,那么同时D的实例调用A的成员函数会有什么情况?
#include <iostream>
using namespace std;
class A{
public:
void test();
private:
int id;
};
void A::test(){
cout <<__func__<<endl;
}
class B:public A{};
class C:public A{};
class D:public B,public C{};
int main(){
cout << "A size:" << sizeof(A) << endl;
cout << "B size:" << sizeof(B) << endl;
cout << "C size:" << sizeof(C) << endl;
cout << "D size:" << sizeof(D) << endl;
}
输出结果:
A size:4
B size:4
C size:4
D size:8
原因:
B与C都继承了A的成员函数test(),D同时继承了B与C,调用test()无法确定时B还是C的。
解决:
给调用的成员函数前加上访问限定符,明确指定调用成员函数所属的类d.B::test();或者d.C::test();
6.3 问题2
不同途径继承来的同名的成员在内存中有不同的拷贝,造成数据不一致
解决:
虚继承和虚基类
虚继承和虚基类:
虚继承:在继承定义中包含了virtual关键字的继承关系
虚基类:在虚继承体系中的通过virtual继承而来的基类
class 类名:public virtual 基类{
}
虚基类是一个相对概念,在虚继承关系中,父类相对于子类是虚基类
6.4 测验
写出下列程序的执行结果,并分析结果。如果编译出错,分析原因并写出解决方案
#include <iostream>
using namespace std;
class Base{
public:
Base(){
cout << "Base constuct" << endl;
}
~Base(){
cout << "Base destuct" <<endl;
}
};
class Member{
public:
Member(){
cout << "Member constuct" << endl;
}
~Member(){
cout << "Member destuct" << endl;
}
};
class Derive:public Base{
public:
Derive(){
cout << "Derive constuct" << endl;
}
~Derive(){
cout << "Derive destuct" << endl;
}
private:
Member m;
};
int main(){
Derive d;
}
运行结果:
Base constuct
Member constuct
Derive constuct
Derive destuct
Member destuct
Base destuct
7、关于多重继承
1、什么是多重继承?同时继承多个父类
2、多重继承有什么危害?菱形继承/钻石继承
3、什么是菱形继承/钻石继承?多重继承的两个或多个父类具有相同的祖先类
4、菱形继承/钻石继承有什么危害?因为多重继承的两个或多个父类具有相同的祖先类,所以会有完全相同的属性或方法。因此当前多重继承有两份相同的属性和方法。使用时会出现冲突。
5、如何解决菱形继承/钻石继承导致的冲突?使用虚继承
6、什么是虚继承?父类在继承具有相同的祖先类时,加上virtual
8、对象构造顺序总结
基本原则:
1、先父后子
2、从左到右
3、先虚后实
4、从上到下
5、由内及外
示例:
#include <iostream>
using namespace std;
#define SIMPLE_CLASS(name)\
class name{\
public:\
name(){cout << #name << "Constructor" << endl;}\
~name(){cout << #name << "Destructor" << endl;}\
};
SIMPLE_CLASS(Base1)
SIMPLE_CLASS(Base2)
SIMPLE_CLASS(Base3)
SIMPLE_CLASS(VBase1)
SIMPLE_CLASS(VBase2)
SIMPLE_CLASS(VBase3)
SIMPLE_CLASS(Member1)
SIMPLE_CLASS(Member2)
SIMPLE_CLASS(Member3)
#undef SIMPLE_CLASS
class Test:public Base1,
public Base2,
public Base3,
public virtual VBase1,
public virtual VBase2,
public virtual VBase3{
public:
Test(){cout << "Test Constructor" << endl;}
~Test(){cout << "Test Destructor" << endl;}
private:
Member1 m1;
Member2 m2;
Member3 m3;
};
int main(){
Test t;
}
运行结果:
VBase1Constructor
VBase2Constructor
VBase3Constructor
Base1Constructor
Base2Constructor
Base3Constructor
Member1Constructor
Member2Constructor
Member3Constructor
Test Constructor
Test Destructor
Member3Destructor
Member2Destructor
Member1Destructor
Base3Destructor
Base2Destructor
Base1Destructor
VBase3Destructor
VBase2Destructor
VBase1Destructor