当前位置:网站首页>继承的所有特征
继承的所有特征
2022-06-09 21:36:00 【爱学代码的学生】
目录
1. 什么是继承?
继承是面向对象的三大特征之一
继承可以理解为一个类从另一个类获取成员变量和成员函数的过程。
例如类 B 继承于类 A,那么 B 就拥有 A 的成员变量和成员函数。被继承的类称为父类或基类,继承的类称为子类或派生类。
2. 继承的基本语法
class 类名:权限 父类名
//孩子有父亲的特点,并且孩子还有自身独特的特点
//父类,也称为基类
class BasePage
{
public:
void header()
{
cout << "首页、公开课、登录、注册..." << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图" << endl;
}
void left()
{
cout << "分类" << endl;
}
void content()
{
cout << "java学科视频" << endl;
}
};
//子类,也成为派生类
class Java :public BasePage
{
public:
void content()
{
cout << "java学科视频" << endl;
}
};
class CPP :public BasePage
{
public:
void content()
{
cout << "CPP学科视频" << endl;
}
};
void Test1()
{
Java a;
cout << "Java网站:" << endl;
a.header();
a.footer();
a.left();
a.content();
}
void Test2()
{
CPP a;
cout << "CPP网站:" << endl;
a.header();
a.footer();
a.left();
a.content();
}
int main()
{
Test1();
cout << "--------------------" << endl;
Test2();
return 0;
}
我们继承了父类中的成员函数和成员变量,并且子类中也具有它们独特的性质。
3. 继承方式
C++中的继承分为三种:1. public继承 2. protect继承 3. private继承
3.1 public继承
//父类
class A
{
public:
int a;
protected:
int b;
private:
int c;
};
//1.公共继承
class Son1 :public A
{
public:
void func()
{
a = 10; //父类的公共权限 在子类仍是公共权限
b = 20; //父类的保护权限 在子类仍是保护权限
c = 30; //父类的私有权限 子类访问不了
}
};
void Test1()
{
Son1 s;
cout << s.a << " " << s.b << endl; //保护权限在类外无法访问
cout << s.c << endl;
}
我们可以发现当是public继承时:
| 成员函数 | 继承前权限 | 继承后权限 |
| a | public | public |
| b | protected | protected |
| c | private | private |
如果是公共继承时,则子类继承的成员权限仍和父类相同,但子类不可以访问父类中的private成员
3.2 protected继承
class A
{
public:
int a;
protected:
int b;
private:
int c;
};
//2.保护继承
class Son2 :protected A
{
public:
void func()
{
a = 10; //父类的公共权限 在子类是保护权限
b = 20; //父类的保护权限 在子类仍是保护权限
c = 30; //父类的私有权限 子类访问不了
}
};
void Test2()
{
Son1 s;
cout << s.a << " " << s.b << endl; //保护权限在类外无法访问
cout << s.c << endl;
}
我们可以发现当是protected继承时:
| 成员函数 | 继承前权限 | 继承后权限 |
| a | public | protected |
| b | protected | protected |
| c | private | private |
如果是保护继承时,父类中为public的成员会降到protected,但子类不可以访问父类中的private成员
3.3 private继承
class A
{
public:
int a;
protected:
int b;
private:
int c;
};
//3.私有继承
class Son3 :private A
{
public:
void func()
{
a = 10; //父类的公共权限 在子类是私有权限
b = 20; //父类的保护权限 在子类是私有权限
//c = 30; //父类的私有权限 子类访问不了
}
};
void Test3()
{
Son3 s;
cout << s.a << endl; //私有权限在类外无法访问
cout << s.b << endl; //私有权限在类外无法访问
cout << s.c << endl;
}
我们可以发现当是private继承时:
| 成员函数 | 继承前权限 | 继承后权限 |
| a | public | private |
| b | protected | private |
| c | private | private |
如果是私有继承时,父类中权限大于private的成员会降到private,但子类不可以访问父类中的private成员
4. 继承中的对象模型
上面我们说到如果父类中有private修饰的成员,在子类中将不允许被访问,难道在子类中就意味着没有继承这个成员吗?
//父类
class Base
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
//子类
class Son1 :public Base
{
public:
/*void func()
{
}*/
private:
int m_d;
};
void test()
{
Son1 s;
cout << "sizeof(s) = " << sizeof(s) << endl;
}
int main()
{
test();
return 0;
}
Ee
我们发现子类的大小为16个字节,说明子类中是继承了父类中private修饰的成员
注意:
在父类中所有非静态的成员都会继承给子类
私有的属性被编译器隐藏了,但仍被继承给子类
通过此图我们也可以更加确定:

子类Son1继承了父类Base中所有的成员
5. 构造和析构的顺序
每个类中都存在着构造和析构,那么当进行继承的时候,构造和析构又是怎么进行的呢?
//继承中的构造和析构函数
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 s;
//Base b;
}
int main()
{
test();
return 0;
}
我们发现首先调用的是父类Base的构造函数,其次调用子类Son的构造函数,这也很好理解,因为没有父类是无法进行继承的
对于析构函数的调用顺序我们只需要记住:析构函数的调用顺序与构造函数的调用顺序相反
6. 同名函数的处理
6.1 同名成员变量
//继承中出现同名的成员
class Base
{
public:
Base()
{
m_a = 10;
}
int m_a;
};
class Son :public Base
{
public:
Son()
{
m_a = 20;
}
int m_a;
};
//同名成员变量
void Test()
{
Son s;
cout << "m_a = "<<s.m_a << endl;
}
int main()
{
Test2();
return 0;
}
我们发现当默认调用m_a时,调用的是子类中的成员变量,那如何调用父类中同名的成员变量呢?
这里我们需要在调用m_a之前添加作用域: 
这样就可以实现调用父类中同名的成员变量
6.2 同名成员函数
//继承中出现同名的成员
class Base
{
public:
Base()
{
m_a = 10;
}
void func()
{
cout << "Base - func" << endl;
}
int m_a;
};
class Son :public Base
{
public:
Son()
{
m_a = 20;
}
void func()
{
cout << "son - func" << endl;
}
int m_a;
};
//同名成员函数
void Test2()
{
Son s;
s.func();
}如果我们直接调用func函数:

会去调用子类中同名的函数,而不是去调用父类中的函数,那该如何调用父类中的同名函数呢?
这里我们仍需要添加作用域:

如果我们在父类中进行了重载:
class Base
{
public:
Base()
{
m_a = 10;
}
void func()
{
cout << "Base - func" << endl;
}
void func(int a)
{
cout << "Base - func(int a)" << endl;
}
int m_a;
};这是我们调用func(100),会调用父类中的func(int a)函数吗?
我们发现调用失败了,这是为什么呢?
1. 子类中和父类中的成员函数名相同,会隐藏所有父类的成员函数
2. 如果想访问父类中的成员函数,需要加上作用域

7. 同名静态成员
同名静态成员的原理和同名成员相似
7.1 同名静态成员变量
#include"继承.h"
//同名静态成员
class Base
{
public:
static int m_a;
};
int Base::m_a = 100;
class Son:public Base
{
public:
static int m_a;
};
int Son::m_a = 10;
void Test()
{
//Son s;
//通过对象访问
cout << s.m_a << endl;
cout << s.Base::m_a << endl;
}
int main()
{
Test2();
return 0;
}
但与同名成员不同的是,静态成员有两种访问方式:1. 对象访问 2.类访问
void Test()
{
Son s;
//通过对象访问
cout << s.m_a << endl;
cout << s.Base::m_a << endl;
通过类访问
cout << Son::m_a << endl;
cout << Base::m_a << endl;
第一个:: 表示通过类名的方式来访问 第二个 :: 表示访问父类中的静态成员变量
cout << Son::Base::m_a << endl;
}
7.2 同名静态成员函数
//同名静态成员
class Base
{
public:
static void func()
{
cout << "Base static void fun " << endl;
}
static void func(int a)
{
cout << "Base static void fun(int a) " << endl;
}
static int m_a;
};
int Base::m_a = 100;
class Son:public Base
{
public:
static void func()
{
cout << "Son static void fun " << endl;
}
static int m_a;
};
int Son::m_a = 10;
void Test2()
{
Son s;
//通过对象访问
s.func();
s.Base::func();
//通过类访问
Son::func();
Base::func();
//第一个:: 表示通过类名的方式来访问 第二个 :: 表示访问父类中的静态成员变量
Son::Base::func();
Son::Base::func(100);
}
int main()
{
Test();
return 0;
}对于静态同名函数,除了增加了类访问,其余操作与非静态成员函数相同
8. 继承语法
之前我们讲述的都是建立在继承一个父类的情况下,如果我们要继承多个父类呢?
这就被称为多继承
多继承语法:class 类名:继承权限 类名,继承权限 ,....
class Base1
{
public:
Base1()
{
m_a = 10;
}
int m_a;
};
class Base2
{
public:
Base2()
{
m_b = 100;
}
int m_b ;
};
class Son :public Base1, public Base2
{
public :
Son()
{
m_c = 20;
m_d = 15;
}
int m_c;
int m_d;
};
void Test()
{
Son s;
cout <<"Base1中的m_a = "<< s.m_a << endl;
cout <<"Base2中的m_b = "<< s.m_b << endl;
}
但是使用多继承的时候,可能会出现父类中存在同名成员,那我们如何访问不同类中的同名成员呢?
这是我们就想到了作用域:

9. 菱形继承问题
9.1 什么是菱形继承?
两个派生类继承一个基类
一个子类同时继承两个派生类
这样的继承方式被称作菱形继承或者钻石继承
9.2 菱形继承带来的问题
1. 菱形继承带来的主要问题就是子类继承了两份的数据,造成了资源浪费
2. 子类中继承了两份数据,构成了二义性
//动物类
class Animal
{
public:
/*Animal()
{
m_age = 10;
}*/
int m_age;
};
//羊类
class Sheep: public Animal
{
};
//骆驼类
class Camel: public Animal
{
};
//羊驼类
class Alpaca: Sheep, public Camel
{
};
int main()
{
Alpaca a;
a.Sheep::m_age = 10;
a.Camel::m_age = 20;
//当出现菱形继承时,两个父类拥有相同的数据,需要加作用域区分
cout <<"a.Sheep::m_age = "<< a.Sheep::m_age << endl;
cout <<"a.Camel::m_age = "<< a.Camel::m_age << endl;
cout << "sizeof(a) = " << sizeof(a) << endl;
//菱形继承导致资源浪费
return 0;
}
当使用菱形继承时,我们会发现子类中继承的m_age存在二义性,而我们只需要一个m_age,那我们需要怎么做呢?
9.3 虚继承
//动物类 虚基类
class Animal
{
public:
/*Animal()
{
m_age = 10;
}*/
int m_age;
};
//羊类
class Sheep: virtual public Animal
{
};
//被virtual修饰过后
// 子类继承的是vbptr virtual base pointer -> vbtable
//骆驼类
class Camel: virtual public Animal
{
};
//羊驼类
class Alpaca: public Sheep, public Camel
{
};
int main()
{
Alpaca a;
a.Sheep::m_age = 10;
a.Camel::m_age = 20;
cout <<"a.m_age = "<< a.m_age << endl;
cout << "sizeof(a) = " << sizeof(a) << endl;
return 0;
}
这时我们发现,Alpaca可以直接调用m_age了,接下来让我们看一张图片:

当被virtual修饰过的类,被称作虚基类,这时类中产生一个叫vbptr(virtual base pointer)的指针,类中的指针指向的都是Alpaca中继承的m_age变量,这样就保证了整个类中只存在一份变量,解决了菱形问题造成的资源浪费
边栏推荐
- Function object (functor)
- 2022 safety production month activity starts safety production and epidemic prevention and control
- spider pi 智能视觉六足机器人自动避障 0604
- Actions before purchasing memory modules
- St link V2 Download: internal command error & error: flash download failed - target DLL has been canceled
- Début de la production de sécurité et prévention et contrôle des épidémies
- How to modify enum type values of fields in MySQL
- 2022安全生产月活动启动安全生产与疫情防控两手抓
- Intelligent prevention and control of safety production risk at construction site in flood season
- 体系化目标一健身合辑
猜你喜欢

Wenxin Ernie 3.0 blessing! Small samples can also achieve 99% effect of full data!

Spider PI intelligent vision hexapod robot color recognition function 0603

spider pi 智能视觉六足机器人 颜色追踪 人脸识别 0604

JS basic data type and reference data type

Slightly more complex queries

spider pi 智能视觉六足机器人 巡路功能 0603

Cookies and session workflows

易买网开发 趣买买 项目资源一览 0605

调查显示macOS应用开发者普遍表示产品如何被用户发现是他们最大的挑战

Digital engineering construction enterprises carry out "safety production month" activities in this way
随机推荐
2022 safety production month activity starts safety production and epidemic prevention and control
node.js 连接sqlserver封装mssql
河北恒银期货是正规平台吗?安全吗?
Début de la production de sécurité et prévention et contrôle des épidémies
易买网开发 趣买买 项目资源一览 0605
实验一:在FW上配置静态路由实现互通
JS basic data type and reference data type
怎么查期货平台是不是安全的?正规性怎么查
化工企业双重预防体系数字化综合管理系统
购买内存条前的行动
Chez scheme environment setup
Why fitness?
scratch编程 飞翔的小鸟 开发笔记 0604
The attribute inheritance of constructor in Es5 borrows the parent constructor method to inherit the prototype object
Meanings of combined reactive power 1 and combined reactive power 2 in gateway meter parameters of power plant enterprises -- meter reading and data acquisition
邦纳雷达传感器Q120RAQ-CN-AF19719
FPN-Feature Pyramid Network
Campus Ruijie router User Guide
datagridview的基本使用 0526
PostgreSQL近期常用的表结构查询语句