一.继承概念
继承就是在原有成员基础上添加特有的成员形成派生类
1.继承的作用
目的:代码重用提高执行效率
关键词:基类(父类)、派生类(子类)
意义:子类拥有父类的成员、子类拥有父类没有的特性、子类是一种特殊的父类(子类可以当做父类使用)
2.继承注意点
- 不能继承父类的构造析构和友元函数
- 子类拥有父类所有成员属性和行为
- 一个类可以有多个基类,也可以有多个派生类
3.继承
派生类成员包括两大部分:一是从基类继承下来的成员(与基类拥有共同的特性)、二是本身独有的特性
格式:
class B:public A{ //类B公有继承类A
};
二、继承分类
1.单一继承
1)公有继承
公有继承下来权限不变。公有即公有、保护即保护、私有即私有
2)保护继承
保护继承:父类原先公有、保护权限在子类中变成保护权限。------类内可以访问,类外不可以访问。
父类原先私有权限在子类中为私有权限。------类内类外都不可以访问
3)私有继承
私有继承:父类原先公有、保护权限在子类中变成私有权限。------类内可以访问,类外不可以访问。
父类原先私有权限在子类中为私有权限。------类内类外都不可以访问
2.派生类成员权限恢复操作
适用范围:对于private和protected继承方式,基类的public成员变成private或者protected。
using 基类名::成员名(行为也只写名,不加括号)
注意:访问声明仅能将继承的成员恢复到原来的访问权限
class base{
public:
base():A(10),B(20),C(30)
{
}
int A;
void fun()
{
cout<<"fun"<<endl;
}
protected:
int B;
private:
int C;
};
//派生类
class home:protected base{
public:
//using 基类名::成员名;//成员函数名后不要加上()
using base::A;
using base::fun; //
void function()
{
fun();
cout <<A<<endl;
}
};
3.继承构造与析构顺序
子类实例化:
4.继承同名函数处理
无论静态成员还是非静态成员。
访问子类:直接加"."访问
访问父类:加"::"作用域
5.基类与派生类之间类型转换
- 派生类对象可以隐式转换为基类对象
#include<iostream>
using namespace std;
class Pason
{
public:
void test()
{
cout<<"父类"<<endl;
}
private:
};
class pason:public Pason{
public:
void test()
{
cout<<"子类"<<endl;
}
private:
};
void Test(Pason a)
{
a.test();
}
int main()
{
pason aa;
Test(aa);
}
- 派生类的对象可以初始化基类的引用;
int main()
{
pason aa;//派生类
Pason &bb = aa; //基类
bb.test();//基类
}
- 派生类的指针可以隐含转换为基类的指针(注意子类析构问题)
int main()
{
pason aa;//派生类
Pason *bb = new pason; //基类
bb->test();//基类
}
2.多继承
class 子类名:public A,public B,public C{};
注意问题:要加作用域区分同名函数,多继承一般不推荐使用
多继承情况下,先构造父类(从左到右),在构造子类。先析构子类,在析构父类。
3.菱形继承
菱形继承: A B同时继承N,他们有共性成员aa,当C多继承A和B,aa也被继承下来。此时调用aa不知道是A.aa还是B.aa ----- 二义性
解决办法:虚继承(将共同的基类设置为虚基类)
虚基表:用来存放虚基类的偏移量,以NULL结尾
使用方法:
class N{
public:
void Display_N()
{
cout<<"N::name:"<<&name<<endl;
cout<<"N::car:"<<&car<<endl;
cout<<"N::lover:"<<&lover<<endl;
}
int name;
};
class A:virtual public N{
public:
void Display_A()
{
cout<<"A::name:"<<&name<<endl;
cout<<"A::car:"<<&car<<endl;
cout<<"A::lover:"<<&lover<<endl;
}
};
class B:virtual public N{
public:
void Display_B()
{
cout<<"B::name:"<<&name<<endl;
cout<<"B::car:"<<&car<<endl;
cout<<"B::lover:"<<&lover<<endl;
}
};
class C:public A,public B{
public:
protected:
};
三.注意点
1.多继承构造顺序
所有的虚基类按照他们继承的顺序构造---->所有的非虚基类按照他们继承的顺序构造---->派生类内所有的子对象按照他们定义的顺序构造---->派生类自己的构造
#include <iostream>
using namespace std;
class A{
public:
A()
{
cout<<"A的构造函数"<<endl;
}
};
class B{
public:
B()
{
cout<<"B的构造函数"<<endl;
}
};
class C{
public:
C()
{
cout<<"C的构造函数"<<endl;
}
};
class D: public A,virtual public B{
public:
C cc; //子对象
D()
{
cout<<"D的构造函数"<<endl;
}
};
int main()
{
D dd;
}
2.在派生类的构造函数成员初始化列表可以为
调用基类的构造函数
派生类中子对象初始化
class D: public C{ //派生类
public:
B b;
D():b(10)//派生类子对象初始化
{
cout<<"D的构造函数"<<endl;
}
};
派生类中一般数据成员的初始化
调用基类的子对象的初始化会出错,应该在基类中进行初始化