当前位置:网站首页>继承的所有特征
继承的所有特征
2022-06-11 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变量,这样就保证了整个类中只存在一份变量,解决了菱形问题造成的资源浪费
边栏推荐
猜你喜欢

UML系列文章(29)体系结构建模---模式和框架

【 C Advanced language】 Integer Storage in Memory

Release of version 5.6 of rainbow, add multiple installation methods, and optimize the topology operation experience

如何利用RPA机器人开启货代行业数字化转型第一步?

LeetCode-104-二叉树的最大深度

【C语言进阶】整型在内存中的存储

Codeforces Round #744 (Div. 3) 解题报告

zypper命令使用示例
![BZOJ3189 : [Coci2011] Slika](/img/46/c3aa54b7b3e7dfba75a7413dfd5b68.png)
BZOJ3189 : [Coci2011] Slika

Iros 2021 | new idea of laser vision fusion? Lidar intensity diagram +vpr
随机推荐
Leetcode-110-balanced binary tree
[Part 16] copyonwritearraylist source code analysis and application details [key]
How to create the simplest SAP kyma function
JVM|前言介绍
How to import workflows provided on SAP API hub to sap BTP
如何查看win系统的安装日期
[v2.1] automatic update system based on motion step API (repair bug, increase completion display, support disconnection reconnection and data compensation)
Only 38 years old! Zhou Chuan, a researcher of the Chinese Academy of Sciences, died unfortunately. Rao Yi sent a document to mourn: he guided me when he was still my student
LeetCode-104-二叉树的最大深度
Redis transaction
How does the chief financial officer of RPA find the "super entrance" of digital transformation?
Experiment 10 Bezier curve generation - experiment improvement - interactive generation of B-spline curve
一个Golang的私有库设置问题
如何使用 SAP Kyma 控制台手动发送 SAP Commerce Cloud Mock 应用暴露的事件
Parker plunger pump pv180r1k1t1nmmc
JVM | local method interface; Native Method Stack
BZOJ3189 : [Coci2011] Slika
RPA+低代码助推品牌电商启新创变、重启增长
为什么需要微服务
JVM | virtual machine stack (local variable table; operand stack; dynamic link; method binding mechanism; method call; method return address)