在我们的生活中,很多事物都有一个最初的模板。比如,房子、车子等等,他们都有相关的房子胚型(屋顶+墙壁...)、车子图纸(框架+四个轮子+车灯...)。大家更着我的脚步,一步一个脚印。作为学者的我们,经常和学习网站打交道(bibilili、大学Mooc等),他们各自的网站,都有公共的头部、公共的底部等公共区域,只有一些中心内容随着我们搜索的内容而改变。
接下来,我们分别用普通写法和继承写法来实现bibilili中的内容,我们可以从两者实现的代码区别来分析得到继承存在的意义和好处。
①普通写法:
// Java搜索页面
class Java
{
public:
void header()
{
cout << "首页、番剧、直播、游戏中心...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void content()
{
cout << "JAVA视频" << endl;
}
};
// 游戏搜索页面
class Game
{
public:
void header()
{
cout << "首页、番剧、直播、游戏中心...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void content()
{
cout << "游戏Game视频" << endl;
}
};
// 电视剧搜索页面
class Tv
{
public:
void header()
{
cout << "首页、番剧、直播、游戏中心...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void content()
{
cout << "电视剧视频" << endl;
}
};
②继承写法:
#include
using namespace std;
// 继承实现页面
// 公共页面类
class BasePage
{
public:
void header()
{
cout << "首页、番剧、直播、游戏中心...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
};
// 继承的好处:减少重复代码
// 语法: class 子类:继承方式 父类
// 子类 又称为 派生类
// 父类 又称为 基类
// Java 页面
class Java:public BasePage
{
public:
void content()
{
cout << "JAVA视频" << endl;
}
};
// 游戏页面
class Game:public BasePage
{
public:
void content()
{
cout << "游戏Game视频" << endl;
}
};
// 电视剧页面
class Tv:public BasePage
{
public:
void content()
{
cout << "电视剧视频" << endl;
}
};
void test01()
{
cout<<"Java页面如下:"<
总结:
class A : public B;
A 类称为子类 或 派生类
B 类称为父类 或 基类
派生类中的成员,包含两大部分:
① 一类是从基类继承过来的;
② 一类是自己增加的成员。
从基类继承过过来的表现其共性,而新增的成员体现了其个性。
语法:class 子类 : 继承方式 父类
继承方式:
继承方式 | 语法 | 特点 |
---|---|---|
公共继承 | class 子类 : public 父类 |
|
保护继承 | class 子类 : protected 父类 |
|
私有继承 | class 子类 : private 父类 |
|
代码案例如下:
#include
using namespace std;
// 继承方式
// 公共继承
class Basedata
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1:public Basedata
{
public:
void func()
{
m_A = 10; // 父类中的公共权限成员 到 子类中依然是公共权限
m_B = 10; // 父类中的保护权限成员 到 子类中依然是保护权限
// m_C = 10; // 父类中的私有权限成员 子类无法访问
}
};
void test01()
{
Son1 s1;
s1.m_A = 23;
// s1.m_B = 24; // 到Son1中 m_B是保护权限,类外访问不到
}
// 保护继承
class Son2:protected Basedata
{
public:
void func()
{
m_A = 23; // 父类中的公共成员,到子类中变为保护权限
m_B = 24; // 父类中的保护权限,到子类中仍为保护权限
// m_C = 25; // 父类中的私有权限成员 子类无法访问
}
};
void test02()
{
Son2 s2;
// s2.m_A = 10; // 到Son2中 m_A变为保护权限,类外访问不到
// s2.m_B = 10; // 到Son2中 m_B保护权限 类外访问不到
}
// 私有继承
class Son3:private Basedata
{
public:
void func()
{
m_A = 23; // 父类中的公共成员,到子类中变为私有权限
m_B = 24; // 父类中的保护权限,到子类中变为私有权限
// m_C = 25; // 父类中的私有权限成员 子类无法访问
}
};
class GrandSon3:public Son3
{
public:
void func()
{
// m_A = 100; // 到了Son3中,m_A变为私有,即使是儿子,也是访问不到
// m_B = 100; // 到了Son3中,m_A变为私有,即使是儿子,也是访问不到
}
};
int main()
{
system("pause");
return 0;
}
从父类继承过来的成员,父类中所有非静态成员属性都会被子类继承下去。
代码案例如下:
#include
using namespace std;
// 继承中的对象模型
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son:public Base
{
public:
int m_D;
};
// 利用开发人员命令提示工具查看对象模型
// 路径调整到文件下路径
// cl /d1 reportSingleClassLayout类名 文件名
void test01()
{
Son s1;
// 16
// 父类中所有非静态成员属性都会被子类继承下去
// 父类中私有属性 被编译器给隐藏了,因此访问不到,但是确实被继承下去了
cout<<"size of Son = "<< sizeof(s1)<
代码输出如下:
结论: 从本案例中,我们发现子类占用的内存为16,说明了父类中私有属性也被子类继承下去了,只是被编译器给隐藏了,因此访问不到其数据,但是确实是被继承下来了。
子类继承父类后,当创建子类对象,也会调用父类的构造函数。但是我们会想到一个问题,就是父类和子类的构造和析构顺序到底是如何排的?
代码案例如下:
#include
using namespace std;
// 继承中的构造和析构顺序
class Base
{
public:
Base()
{
cout<<"Base构造函数的调用"<
代码输出如下:
总结:继承中 先构造父类,再构造子类;析构的顺序与构造的顺序相反
当子类与父类出现同名的成员,我们通过以下方式来访问子类和父类中同名的成员和函数:
代码案例如下:
#include
using namespace std;
// 继承同名成员处理方式
class Base
{
public:
Base()
{
m_A = 10;
}
void func()
{
cout<<"Base-func()调用"<
总结:
静态成员和非静态成员出现同名,处理方式是一致。其处理方式如下:
代码案例如下:
#include
using namespace std;
// 继承同名静态成员处理方式
class Base
{
public:
static int m_A;
static void func()
{
cout<<"static Base-func()调用"<
总结:同名静态成员处理方式和非静态成员处理方式一样,只不过有两种访问的方式(通过对象 访问和通过类名访问)
C++允许一个类继承多个类
语法:class 子类:继承方式 父类1, 继承方式 父类2...
注意:多继承可能会引发父类中有同名成员出现,需要加作用域区分
代码案例如下:
#include
using namespace std;
// 多继承语法
class Base1
{
public:
Base1()
{
m_A = 99;
}
int m_A;
};
class Base2
{
public:
Base2()
{
m_A = 1;
}
int m_A;
};
// 子类 继承Base1 和 Base2
// 语法: class 子类: 继承方式 父类1, 继承方式 父类2...
class Son:public Base1,public Base2
{
public:
Son()
{
m_C = 199;
m_D = 288;
}
int m_C;
int m_D;
};
void test01()
{
Son s1;
cout<<"size of Son = "<< sizeof(s1)<
代码输出如下:
总结:在C++实际开发中,不建议用多继承语法的。而且,在多继承中,如果父类中出现了同名情况,则子类使用的时候一定要加对应的作用域哦。
概念:两个派生类继承同一个基类,又有某个类同时继承者两个派生类,这种继承被称为菱形继承(钻石继承)。
案例:羊和驼继承同一个基类(动物),又有羊驼同时继承者两个派生类(羊和驼)
问题:
代码案例如下:
#include
using namespace std;
// 菱形继承
// 动物类
class Animal
{
public:
int m_Age;
};
// 利用虚继承 解决菱形继承的问题
// 继承之前 加上关键字 virtual 变为虚继承
// Animal类称为 虚基类
// 羊类
class Sheep:virtual public Animal{};
// 驼类
class Tuo:virtual public Animal{};
// 羊驼类
class YangTuo:public Sheep, public Tuo{};
void test01()
{
YangTuo yt;
yt.Sheep::m_Age = 20;
yt.Tuo::m_Age = 24;
// 当菱形继承,两个父类拥有相同数据,需要加以作用域区分
cout<<"yt.Sheep::m_Age ="<
案例知识点:
总结: