1.继承的三大分类
继承分为三类:
公共继承
保护继承
私有继承
他们的特点如下图所示:
2.继承基本语法
继承的好处: 减少重复代码
语法:
class 子类 : 继承方式 父类
子类 也称 派生类
父类 也称 基类
例1:不使用继承
#include
using namespace std;
//Java页面
class Java {
public:
void header(){
cout << "header" << endl;
}
void footer(){
cout << "footer" << endl;
}
void left(){
cout << "left" << endl;
}
void content(){
cout << "***Java***" << endl;
}
};
class Python {
public:
void header(){
cout << "header" << endl;
}
void footer(){
cout << "footer" << endl;
}
void left(){
cout << "left" << endl;
}
void content(){
cout << "***Python***" << endl;
}
};
class Cpp{
public:
void header(){
cout << "header" << endl;
}
void footer(){
cout << "footer" << endl;
}
void left(){
cout << "left" << endl;
}
void content(){
cout << "***C++***" << endl;
}
};
int main()
{
Java java;
java.header();
java.footer();
java.left();
java.content();
Python python;
python.header();
python.footer();
python.left();
python.content();
Cpp cpp;
cpp.header();
cpp.footer();
cpp.left();
cpp.content();
return 0;
}
我们发现,上述代码有很多重复的部分,十分冗余。
例2:使用继承
#include
using namespace std;
class BasePage{
public:
void header(){
cout << "header" << endl;
}
void footer(){
cout << "footer" << endl;
}
void left(){
cout << "left" << endl;
}
void content(){
cout << "***basepage***" << endl;
}
};
//Java页面
class Java : public BasePage{
public:
void content(){
cout << "***Java***" << endl;
}
};
class Python : public BasePage{
public:
void content(){
cout << "***Python***" << endl;
}
};
class Cpp : public BasePage{
public:
void content(){
cout << "***C++***" << endl;
}
};
int main()
{
Java java;
java.header();
java.footer();
java.left();
java.content();
Python python;
python.header();
python.footer();
python.left();
python.content();
Cpp cpp;
cpp.header();
cpp.footer();
cpp.left();
cpp.content();
return 0;
}
从上面两个例子不难看出,使用继承后代码看起来更加简洁易懂,省去了很多重复代码.
3.继承中的对象模型
4.继承中的构造和析构顺序
例3:
#include
using namespace std;
class Base{
public:
Base(){
cout << "Base的构造函数" << endl;
}
~Base(){
cout << "Base的析构函数" << endl;
}
};
class Son : public Base{
public:
Son(){
cout << "Son的构造函数" << endl;
}
~Son(){
cout << "Son的析构函数" << endl;
}
};
int main()
{
Son s;
}
运行结果:
由运行结果我们可知:先构造父类,在构造子类,析构的顺序和构造的顺序相反
5.继承同名成员处理方式
当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢
- 访问子类同名成员 直接访问即可
- 访问父类同名成员 需要加作用域
例4:
#include
using namespace std;
class Base{
public:
int m_A;
public:
Base(){
m_A = 100;
}
void func(){
cout << "Base * func()调用" << endl;
}
void func(int a){
cout << "Base * func(int a)调用" << endl;
}
};
class Son : public Base{
public:
int m_A;
public:
Son(){
m_A = 200;
}
void func(){
cout << "Son * func()调用" << endl;
}
};
//同名成员属性
void test01(){
Son s;
cout << "Son 下的m_A = " << s.m_A << endl;
cout << "Base 下的m_A = " << s.Base::m_A << endl;
}
//同名成员函数
void test02(){
Son s;
s.func();
s.Base::func();
s.Base::func(100);
}
int main()
{
test01();
cout << "*****************" << endl;
test02();
}
运行结果:
总结:
- 子类对象可以直接访问到子类中同名成员
- 子类对象加作用域可以访问到父类同名成员
- 当子类与父类拥有同名的成员函数,子类会隐藏父类中的同名成员函数,加作用域可以访问到父类中同名函数
6.继承中的同名静态成员处理
同名静态成员处理和非静态处理方式一样,只不过有两种访问的方式(通过对象和通过类名)
具体方法如下:
例5:
#include
using namespace std;
class Base{
public:
static int m_A;
public:
Base(){
m_A = 100;
}
static void func(){
cout << "Base * func()调用" << endl;
}
static void func(int a){
cout << "Base * func(int a)调用" << endl;
}
};
class Son : public Base{
public:
static int m_A;
public:
Son(){
m_A = 200;
}
static void func(){
cout << "Son * func()调用" << endl;
}
};
//同名静态成员属性
void test01(){
//通过对象访问
cout << "通过对象访问" << endl;
Son s;
cout << "Son 下的m_A = " << s.m_A << endl;
cout << "Base 下的m_A = " << s.Base::m_A << endl;
//通过类名访问
cout << "通过类名访问" << endl;
cout << "Son 下的m_A = " << Son::m_A << endl;
//第一个::代表通过类名方式访问 第二个::代表访问父类作用域下
cout << "Base 下的m_A = " << Son::Base::m_A << endl;
}
//同名静态成员函数
void test02(){
Son s;
cout << "通过对象访问" << endl;
s.func();
s.Base::func();
cout << "通过类名访问" << endl;
Son::func();
Son::Base::func();
Son::Base::func(100);
}
int Base::m_A = 100;
int Son::m_A = 200;
int main()
{
test01();
cout << "*****************" << endl;
test02();
}
运行结果:
7.多继承语法
语法:
class 子类: 继承方式 父类1,继承方式 父类2...
例6:
#include
using namespace std;
class Base1{
public:
Base1(){
m_A = 100;
}
public:
int m_A;
};
class Base2{
public:
int m_B;
public:
Base2(){
m_B = 200;
}
};
//子类,需继承Base1和Base2
class Son : public Base1, public Base2{
public:
int m_C;
int m_D;
public:
Son(){
m_C = 300;
m_D = 400;
}
};
void test01(){
Son s;
cout << "Sizeof(son) = " << sizeof(s) << endl;
//注:当继承的父类中含有同名成员,需要加作用域区分
//cout << s.Base1::m_A
//cout << s.Base2::m_B
}
int main()
{
test01();
}
8.菱形继承
即:有两个子类B, C继承一个父类A,
又有一个子类D继承B和C
#include
using namespace std;
//动物类
class Animal{
public:
int m_Age;
};
//利用虚继承,可以解决
//继承之前 加上关键字 virtual 变为虚继承
//Animal类称为 虚基类
//羊类
class Sheep : virtual public Animal{};
//驼类
class Camel : virtual public Animal{};
//羊驼
class SheepTuo : public Sheep, public Camel{};
void test01(){
SheepTuo st;
st.Sheep::m_Age = 18;
st.Camel::m_Age = 28;
//当菱形继承,两个父类有相同的数据,需要加以作用域区分
cout << "Sheep::m_Age = " << st.Sheep::m_Age << endl;
cout << "Camel::m_Age = " << st.Camel::m_Age << endl;
cout << "m_Age = " << st.m_Age << endl;
//这份数据我们知道 只需有一份就可以,菱形继承导致数据有两份,资源浪费
}
int main()
{
test01();
}
总结:
- 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费且毫无意义
- 利用虚继承可以解决菱形继承问题
虚继承:子类继承的是两个指针,两个指针会通过偏移量在虚基列表中找到唯一的数据,这个数据只有一个
以上为我总结的C++继承方面的主要知识.
如有不对,敬请指正