广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++ 继承的范例讲解
  • 743
分享到

C++ 继承的范例讲解

2024-04-02 19:04:59 743人浏览 独家记忆
摘要

目录1.继承的概念2.继承方式3.基类与派生类的赋值转换4.作用域与隐藏5.派生类的默认成员函数6.友元与静态成员7.菱形继承与虚继承8.继承和组合1.继承的概念 继承,是面向对象的

1.继承的概念

继承,是面向对象的三大特性之一。继承可以理解成是类级别的一个复用,它允许我们在原有类的基础上进行扩展,增加新的功能。

当创建一个类时,我们可以继承一个已有类的成员和方法,并且在原有的基础上进行提升,这个被继承的类叫做基类,而这个继承后新建的类叫做派生类。

用法如下:

class [派生类名] : [继承类型] [基类名]

例如:

class Person
{
public:
	string _name;
	int _age;
};
class Student : public Person
{
protected:
	string _stuNum;
};

这里的派生类Student就复用了Person的方法和成员,并在此基础上扩展补充。

2.继承方式

继承的方式和类的访问限定符一样,分为public(公有继承),private(私有继承), protected(保护继承)三种。

不同的继承方式,在派生类中继承下来的基类成员的访问权限也不一样。

基类的其他成员在子类的访问方式 = Min(成员在基类的访问限定符,继承方式)

备注:

1.在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能 在派生类的类里面使用,实际中扩展维护性不强。

2.使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。

3.基类与派生类的赋值转换

派生类可以赋值给基类的对象、指针或者引用,这样的赋值也叫做对象切割。

例如上面的Person类和Student类

这种赋值只能是派生类赋给基类(但需要割掉多出来的成员例如_ stuID),而基类对象不能赋给派生类。

基类的指针可以强制类型转换赋值给派生类的指针, 如:

int main()
{
	Person p1;
	Student s1;
	Person* hPtr1 = &s1;//指向派生类对象
	Person* hPtr2 = &p1;//指向基类对象
	Student* pPtr = (Student*)hPtr1;//没问题
	Student* pPtr = (Student*)hPtr2;//有时候没有问题,但是会存在越界风险
	return 0;
}

小结:

1.派生类可以赋值给基类的对象、指针或者引用

2.基类对象不能赋值给派生类对象

3.基类的指针可以通过强制类型转换赋值给派生类的指针。**?但是必须是基类的指针是指向派生类对象时才是安全的,否则会存在越界的风险。**这里基类如果是多态类型,可以使用RTT的dynamic_cast来进行识别后进行安全转换。

4.作用域与隐藏

?隐藏:隐藏,也叫做重定义,当基类和派生类中出现重名的成员时,派生类就会将基类的同名成员给隐藏起来,然后使用自己的。(但是隐藏并不意味着就无法访问,可以通过指明基类作用域来显式访问隐藏成员。)

class Person
{
public:
	void f(int age)
	{
		cout << "姓名" << _name << endl;
		cout << "年龄" << _age << endl;
	}
protected:
	string _name;
	int _age;
};
class Student : public Person
{
public:
	void f()
	{
		Person::f(32);//需显式调用f函数
		cout << "学号" << _stuNum << endl;
	}
private:
	string _stuNum;
};

例如这里的f( )就构成了隐藏

同时,这里还有个需要注意的问题,在基类与派生类中,同名的方法并不能构成重载,因为处于不同的作用域中。而只要满足方法名相同,就会构成隐藏。

5.派生类的默认成员函数

在每一个类中,都会有6个默认的成员函数,这些函数即使我们自己不去实现,编译器也会帮我们实现。

?这里有两点需要注意(笔试题常考):

1.构造函数,拷贝构造,operator=三种情况,都要调用父类对应的构造函数/拷贝构造/operator=进行对父类的成员变量的初始化,并且倘若父类没有默认的构造函数的时候(比如父类写了带参的构造函数),我们就要显式调用(Person(参数…),Person::operator=(参数…))

?构造函数显示调用

派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函 数,则必须在派生类构造函数的初始化列表阶段显示调用。

Student()
    	:People()
	{
		cout << "Student()" << endl;
	}

?拷贝构造显示调用

建议拷贝构造都用显示调用,不然免不了出现子类拷贝构造当中调用了父类的构造函数的情况(因为拷贝构造也是构造,在初始化列表处,对于子类而言,父类相当于一个自定义类型对象,子类会调用父类的构造函数对父类的资源进行初始化。)

Student(const Student& s)
		:People(s)
	{
		cout << "Student(const Student& s)" << endl;
	}

?派生类的operator=必须要调用基类的operator=完成基类的复制。

Student& operator=(const Student& s)
	{
		cout << "Student& operator = (const Student& s)" << endl;
		if (this != &s)
		{
			Person:: operator=(s);
		}
	}

?析构函数

由于编译器会将析构函数的名字处理成destructor,因此派生类和基类的析构函数会构成隐藏关系,故若要派生类要调用基类的析构函数,那么需要显式调用,但是编译器会默认在派生类的析构函数调用结束后调用基类的析构函数,这样就析构两次了。

	~Person()
	{
		cout << "~Person()" << endl;
	}
	~Student()
	{
		Person:: ~Person();
		cout << "~Student()" << endl;
	}

在派生类中,基类的析构函数会被隐藏,虽然它们这里的名字不同,但是为了实现多态, 它们都会被编译器重命名为destructor。在调用子类的构造函数时,我们是先调用父类的构造函数,后对子类的成员进行构造。由先构造后析构的顺序,所以我们是在析构函数当中析构子类的资源,析构函数调用完后编译器自动帮我们调用父类的析构函数。

6.友元与静态成员

?1.友元

友元关系是不会继承的,如果子类要使用父类的友元,则子类自己也要将其定义为友元。

?2.静态成员

基类定义了static静态成员,无论继承了多少次,派生了多少子类,静态成员在这整个继承体系中有且只有一个。静态成员不再单独属于某一个类亦或者是某一个对象,而是属于这一整个继承体系。

7.菱形继承与虚继承

首先简单介绍下单继承、多继承的概念

单继承:一个子类只有一个直接父类时称这个继承关系为单继承

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

菱形继承:菱形继承是多继承的一种特殊情况,下面简单举例介绍下菱形继承及其带来的二义性问题:

class Human
{
public:
	int _age;
};
class Student : public Human
{
public:
	int _stuNum;
};
class Teacher : public Human
{
public:
	int _teaNum;
};
class Assistant : public Teacher, public Student
{
};

哦豁!!!菱形继承这个关系感受到了吧!!!

?直接讲下这个菱形继承带来的二义性问题:

按照道理来说,各个类的大小应该是这样的。human类4个字节,teacher和student都是8个字节,而assistant是12个字节,但是实际上assistant却是16字节。

?为什么assistant会有16字节?

这就是菱形继承的数据冗余和二义性问题的体现。

这里的teacher和student都从human中继承了相同的成员 _age。但是assistant再从teacher和student继承时,就分别把这两个 _age都给继承了过来,导致这里有了两个一样的成员。

在这样的情况下,后续想给 _age赋值,也会被编译器提示指示不明确,报错。

?菱形继承的二义性是很致命的问题,如何解决呢?

虚继承,在腰部的类继承时添加virtual关键字。

class Student : virtual public Human
{
public:
	int _stuNum;
};
class Teacher : virtual public Human
{
public:
	int _teaNum;
};
class Assistant : public Teacher, public Student
{
};

这次,二义性问题解决了,teacher和student都是12个字节,而assistant是20个字节。

想知道为什么?点这里:从内存角度看待虚继承

?简单小结一下

1.可以看到没有虚继承的情况下,Assitant中的成员连续排列出现了Teacher和Student中的_age是两个不同的值,但实际上一个人不会有两个年龄,所以这就出现了数据冗余。

2.这里多出来的8个字节,其实是两个虚基表指针。因为这里Human中的 _age是 teacher和 student共有的,所以为了能够方便处理,在内存中分布的时候,就会把这个共有成员 _age放到对象组成的最末尾的位置。然后通过了Teacher和Student的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的Human。由此可见,使用了虚拟继承后,就可以解决菱形继承导致的问题。

?为什么Teacher、Student需要去找属于自己的 _age?

基类与派生类的赋值转换时,需要进行切片。

int main()
{
	Assistant a;
	Teacher t = a; 
	Student s = a;
	return 0;
}

当把对象a赋值给t和s的时候,因为他们互相没有对方的 _stuNum和 _teaNum,所以他们需要进行对象的切割,但是又因为 _age存放在对象的最尾部,所以只有知道了自己的偏移量,才能够成功的在切割了没有的元素时,还能找到自己的 _age。

8.继承和组合

?继承是一种复用的方式,但不是唯一方式!

1.public继承是一种is-a的关系,就是基类是一个大类,而派生类则是这个大类中细分出来的一个子类,但是他们本质上其实是一种东西。

2.组合是一种has-a的关系,就是一种包含关系,比如对象a是对象b的成员,那么他们的关系就是对象b的组成中包含了对象a,对象a是对象b中的一部分,对象b包含对象a。

3.继承方式的复用常称之为白箱复用,在继承方式中,基类的内部细节对子类可见,这一定程度上破坏了基类的封装,伴随着基类的改变,对派生类的改变很大。并且两者依赖关系强,耦合度大。

4.对象组合式继承之外的复用选择,对象组合要求被组合对象提供良好的接口定义。这种复用称之为黑箱复用,对象的内部实现细节是不可见的。耦合度低。

实际工程中能用继承和组合就用组合,组合的耦合度低,代码的维护性好,但是继承在有些关系就适合用继承就用继承,并且要实现多态就一定要用继承。

到此这篇关于c++ 继承的范例讲解的文章就介绍到这了,更多相关C++ 继承内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++ 继承的范例讲解

本文链接: https://www.lsjlt.com/news/150569.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++ 继承的范例讲解
    目录1.继承的概念2.继承方式3.基类与派生类的赋值转换4.作用域与隐藏5.派生类的默认成员函数6.友元与静态成员7.菱形继承与虚继承8.继承和组合1.继承的概念 继承,是面向对象的...
    99+
    2022-11-13
  • JavaScript继承与多继承实例讲解
    这篇文章主要介绍“JavaScript继承与多继承实例讲解”,在日常操作中,相信很多人在JavaScript继承与多继承实例讲解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”...
    99+
    2022-10-19
  • C++详细讲解继承与虚继承实现
    目录继承的概念及定义概念:定义:继承关系和访问限定符总结基类和派生类对象赋值转换继承中的作用域派生类的默认成员函数继承与友元继承与静态成员复杂的菱形继承及菱形虚拟继承虚继承原理继承的...
    99+
    2022-11-13
  • C++图文并茂讲解继承
    目录一、生活中的例子二、惊艳的继承三、继承的意义四、小结一、生活中的例子 组合关系∶整体与部分的关系 下面看一个组合关系的描述代码: #include <iostream&g...
    99+
    2022-11-13
  • JavaScript类的继承全面示例讲解
    目录1. ES5 中的继承1.1 修改原型链1.2 调用父类的构造函数1.3 组合继承1.4 原型继承1.5 寄生式继承2. ES6 中的继承1. ES5 中的继承 首先假设我们有一...
    99+
    2022-11-13
    JavaScript 类的继承 JavaScript 继承
  • python 面向对象之继承实例讲解
    面向对象编程语言具有封装、继承、多态三个基本特征,本文就继承举例详谈比如说学校主要有3大角色:学校,讲师,学员学校可以注册学员,统计有多少学员老师负责讲课学生听课,提问,学习,交学费先定义4个类:class School(obje...
    99+
    2023-01-30
    面向对象 实例 python
  • C++ 继承,虚继承(内存结构)详解
    目录普通的公有继承多重继承虚继承虚继承(菱形继承)总结普通的公有继承 class test1 { public: test1(int i) :num1(i) {} pri...
    99+
    2022-11-12
  • Java超详细讲解类的继承
    目录写在前面1.子类的创建1.1子类的创建方法1.2调用父类中特定的构造方法2.在子类中访问父类成员3.覆盖3.1覆盖父类中的方法3.2用父类的对象访问子类的成员4.不可被继承的成员...
    99+
    2022-11-13
  • 详解C++中单继承与多继承的使用
    目录前言1.继承的概念和定义(1)继承的概念(2)继承的定义方法(2)继承后子类的成员类型2.基类与派生类的赋值转换(1)派生类赋值给基类(2)基类给派生类3.继承中的作用域(1)隐...
    99+
    2022-11-13
  • JavaScript面向对象之class继承类案例讲解
    1. 面向对象class继承 在上面的章节中我们看到了JavaScript的对象模型是基于原型实现的,特点是简单,缺点是理解起来比传统的类-实例模型要困难,最大的缺点是继承的实现需要...
    99+
    2022-11-12
  • C#中的类继承详解
    目录前言类的继承注意事项寄语总结前言 在日常的程序编码工作中,我们的最终目标是完成公司交给自己的开发任务,核心目标是写出好代码。 那么什么是好代码? 高内聚,低耦合 想必从事编码工作...
    99+
    2022-11-13
  • 举例讲解Python面向对象编程中类的继承
    python创建一个类很简单只需要定义它就可以了. class Cat: pass 就像这样就可以了,通过创建子类我们可以继承他的父类(超类)的方法。这里重新写一下cat class Cat...
    99+
    2022-06-04
    面向对象 Python
  • C++继承方式的示例分析
    这篇文章给大家介绍C++继承方式的示例分析,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。C++支持多种程序设计风格,其中就包括对面向对象设计的支持。我们今天在这里将会为大家详细介绍一下各种C++继承方式的具体应用方法,...
    99+
    2023-06-17
  • C++继承模式详解
    目录继承继承的概念继承的定义继承关系和访限定符继承方式父类和子类对象赋值转化继承中的作用域子类的默认成员函数继承与友元继承与静态成员复杂的菱形继承虚继承继承的总结继承 继承的概念 ...
    99+
    2022-11-12
  • C++多继承(多重继承)的实现
    在前面的例子中,派生类都只有一个基类,称为单继承(Single Inheritance)。除此之外,C++也支持多继承(Multiple Inheritance),即一个派生类可以有...
    99+
    2023-05-16
    C++多继承 C++多重继承
  • 一文读懂C++中的继承之菱形继承(案例分析)
    目录前言一、什么是多继承?1.单继承2.多继承二、菱形继承1.虚基类的引入2.虚基表的引入总结前言 我们上一篇说了世间万物都有一个继承体制,或多或少子类继承了父类的某些特征,但大多都...
    99+
    2022-11-12
  • Java由浅入深讲解继承下
    目录1.代码块初始化2.protected关键字3.final关键字4.Java中允许的继承方式本篇紧接上一篇内容继续,还是从继承里的细节开始 1.代码块初始化 关于代码块的定义和使...
    99+
    2022-11-13
  • Java由浅入深讲解继承上
    目录1.什么是继承2.继承的细节2.1super关键字2.2子类的构造方法2.3super和this区别继承同样是面向对象程序的特点 1.什么是继承 所谓继承就是抽取类的共性,进而实...
    99+
    2022-11-13
  • C++类的继承怎么理解
    这篇文章主要讲解了“C++类的继承怎么理解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++类的继承怎么理解”吧!而基类中的公有成员在public派生时,不仅可以由派生类对象成员访问,也可...
    99+
    2023-06-17
  • c++中继承关系的示例分析
    这篇文章给大家分享的是有关c++中继承关系的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1 什么是继承继承概念继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序...
    99+
    2023-06-20
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作