继承和派生其实都是一回事,只是说法不同罢了。
如:子类继承了父类,父类派生了子类。
那么什么是继承呢
首先,如上图所示是一个动物种类区分图。猫和狗都属于动物这一种类,而猫又可以细分为加菲猫、布偶猫等种类。我们发现这些细分出来的单体在拥有上一级别的共性之外,还有各自的共性。比如加菲猫和布偶猫体型、毛发品质不同,但他们都属于猫类。
在编写代码的时候类与类之间也一样,子类拥有上一级类的共性,又拥有自己的特性!这个时候就可以利用继承的特性减少代码的重复性!!
一、基类 & 派生类
当创建一个类时,不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。
例如我们看到很多网站中,都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同。
#include <iostream>
using namespace std;
//基类
class basepage {
public:
void header()
{
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java,Python,C++...(公共分类列表)" << endl;
}
};
//派生类
class CPP :public basepage {
public:
void content() {
cout << "C++学科视频" << endl;
}
};
void test01()
{
//C++页面
cout << "C++下载视频页面如下: " << endl;
CPP cp;
cp.header();
cp.footer();
cp.left();
cp.content();
}
int main() {
test01();
system("pause");
return 0;
}
运行结果:
从运行结果可以看出,CPP类在定义自己的特性基础上还保留basepage类的属性,这就是派生的基本用法。
一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:
class 派生类名:继承方式 基类名
其中继承属性有public、private、protected三种 。
多继承的方式如下:
语法:class 派生类:继承方式 基类1 ,继承方式 基类2
二、继承方式
前面说了类的继承方式有三种public、private、protected,他们的区别就是派生类对基类的访问权限有所不同!
- 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
- 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
- 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
代码如下:
#include <iostream>
using namespace std;
//
//**继承方式一共有三种:**
//
//* 公共继承
//* 保护继承
//* 私有继承
class base1 {
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
//公有派生
class son1 :public base1 {
public:
void func() {
m_a; //可访问public权限
m_b; //可访问protected权限
//m_c; //m_c是基类的私有权限无法访问
}
};
//保护派生
class son2 :protected base1 {
public:
void func() {
m_a; //可访问protected权限
m_b; //可访问protected权限
//m_c; //m_c是基类的私有权限无法访问
}
};
//保护派生
class son3 :private base1 {
public:
void func() {
//m_a; //不可访问private权限
//m_b; //不可访问private权限
//m_c; //m_c是基类的私有权限无法访问
}
};
void test() {
son1 s;
s.m_a;
//s.m_b; 公有派生不改变成员变量的权限,因此m_b是保护权限,类外无法访问
son2 s1;
//s1.m_a; //protected权限,类外无法访问
}
int main()
{
system("pause");
return 0;
}
三、继承的对象模型
从父类继承过来的成员变量,哪些属于子类对象中?我们已经知道当发生公有继承的时候,不会改变基类成员变量的访问权限。
并且在派生类中,无法访问基类中private属性的成员变量。无法访问并不意味着没有继承到派生对象中。可以看到如下代码:
#include <iostream>
using namespace std;
//基类
class base {
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class son1 :private base {
public:
int m_d;
};
void test() {
cout << "son1 的 size = " << sizeof(son1) << endl;
}
int main()
{
test();
system("pause");
return 0;
}
运行结果:
可以看出类son1的大小有16个字节,可以得出结论:派生类会继承基类中的private权限的成员变量。
一个派生类继承了所有的基类方法,但如下三种情况除外:
- 基类的构造函数、析构函数和拷贝构造函数。
- 基类的重载运算符。
- 基类的友元函数。
四、基类和派生类构造/析构函数执行的顺序
#include <iostream>
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;
}
};
void test() {
son b1;
}
int main()
{
test();
system("pause");
return 0;
}
运行结果:
从运行结果可以看出在派生中,先执行基类的构造函数,再执行派生类的构造函数(肯定现有爸爸,才有儿子嘛)。析构函数顺序则与前者相反!
五、同名成员变量(函数)的处理
有时候我们会遇到派生类和基类出现同名成员函数和成员变量的问题,这时派生类会隐藏基类类中所有版本的同名成员函数。
即在派生类定义的对象中访问同名成员变量(函数)的时候,执行的是派生类中。如果想访问基类中的成员变量(函数),就需要基类作用域。
#include <iostream>
using namespace std;
class base {
public:
base() {
m_a = 100;
}
void func() {
cout << "base -func()的调用" << endl;
}
void func(int a) {
cout << "base -func(int a)的调用" << endl;
}
public:
int m_a;
};
class son :public base {
public:
son() {
m_a = 200;
}
//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数
//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
void func()
{
cout << "Son - func()调用" << endl;
}
public:
int m_a;
};
void test01() {
son s;
cout << "son下的 m_a:" << s.m_a << endl;
cout << "base下的 m_a:" << s.base::m_a << endl;
s.func();
s.base::func();
s.base::func(10);
}
int main()
{
test01();
system("pause");
return 0;
}
运行结果:
六、同名静态成员变量(函数)的处理
除了遇到同名成员变量(函数),有时还会遇到同名的静态成员变量(函数)。与前者相似,为了访问基类的同名对象,需要加上作用域。只不过静态成员变量(函数)有两种访问方式:
①通过对象访问
②通过类名访问
#include <iostream>
using namespace std;
class base {
public:
static void func() {
cout << "base static void func()" << endl;
}
static void func(int a) {
cout << "base static void func(int a)" << endl;
}
public:
static int m_a;
};
int base::m_a = 10; /*类的静态成员变量,类内声明,类外定义*/
class son:public base {
public:
static void func() {
cout << "son static void func()" << endl;
}
public:
static int m_a;
};
int son::m_a = 20;
//同名静态成员属性
void test01() {
//通过对象访问
cout << "通过对象访问" << endl;
son s1;
cout << "son 下的m_a:" << s1.m_a << endl;
cout << "base 下的m_a:" << s1.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() {
//通过对象访问
cout << "通过对象访问静态同名成员函数" << endl;
son s2;
s2.func();
s2.base::func();
//通过类名访问
son::func();
son::base::func();
son::base::func(10);
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
运行结果:
七、菱形继承问题
菱形继承,虚继承_audience_fzn的博客-CSDN博客
两个派生类继承同一个基类,又有某个类同时继承两个派生类这种继承被称为菱形继承。这个时候,就会发生数据的二义性。
因为最后的类继承了2次,原始基类的特性。为了解决这种情况引入了virtual关键字。代码如下:
#include <iostream>
using namespace std;
class animal {
public:
int m_age;
};
//sheep继承animal
class sheep:virtual public animal {
};
class Tuo:virtual public animal {
};
class sheepTuo:public sheep, public Tuo {
};
void test01() {
sheepTuo s;
s.sheep::m_age = 100;
s.Tuo::m_age = 200;
cout << "s.sheep::m_age=" << s.sheep::m_age << endl;
cout << "s.Tuo::m_age=" << s.Tuo::m_age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
继承前加virtual关键字后,变为虚继承。此时公共的父类Animal称为虚基类
运行结果: