C++ 继承(二)

news2024/9/27 12:16:32

目录

1. 实现一个不能被继承的类

 2. 友元与继承

 3.继承与静态成员

4.多继承及其菱形继承问题

(1). 继承模型

(2). 虚继承

(2.1)虚继承解决数据冗余和二义性的原理

(3). 多继承中指针偏移问题

(4). IO库中的菱形虚拟继承

5. 继承和组合


1. 实现一个不能被继承的类

方法1:父类的构造函数私有,子类构成必须调用父类的构造函数,但是父类的构造函数私有后,子类就不能调用了。那子类将无法实例化处对象。

 如下代码所示

#include<iostream>
#include<algorithm>

using namespace std;
class Teacher
{
private:
	Teacher()
	{
		x = 1;
	}
	int x;
};

class Student: public Teacher
{
public:
	Student()
	{
		ss = 1;
	}
private:
	int ss;
};

 因为父类构造函数不能调用出错

 

方法2:C++11中新增了final关键字,在父类,类名后加上final修饰子类就不能继承了

 代码如下

#include<iostream>
#include<algorithm>

using namespace std; 
class Teacher final
{
public:
	Teacher()
	{
		x = 1;
	}
private:
	int x;
};

class Student: public Teacher
{
public:
	Student()
	{
		ss = 1;
	}
private:
	int ss;
};

 不可以将其当做基类(父类)

 2. 友元与继承

友元关系是不能继承的,也就是说父类的友元不能访问子类私有和保护成员

 代码如下

#include<iostream>
#include<algorithm>

using namespace std; 

class Student;//提前声明否则友元函数定义找不到Student
class Teacher 
{
	
public:
	friend void playval( const Teacher& t, const Student& s);
	Teacher(int xx=11)
	{
		x = xx;
	}
private:
//protected:
	int x;
};

class Student: public Teacher
{
public:
	Student()
		:Teacher(11)
	{
		ss = 1;
	}
private:
	int ss;
};

void playval(const Teacher& t,const Student& s )
{
	cout << t.x << endl;
	cout << s.ss << endl;
}


int main()
{
	Teacher t(16);
	Student s;
	playval(t, s);
}

结果如图,不能找到s的私有成员

 当然子类的友元也不能访问父类的保护和私有成员

 如下代码所示

#include<iostream>
#include<algorithm>

using namespace std; 


class Teacher 
{
	
public:
	Teacher(int xx=11)
	{
		x = xx;
	}
private:
//protected:
	int x;
};

class Student: public Teacher
{
public:
	friend void inputval(const Teacher& t, const Student& s);
	Student()
		:Teacher(11)
	{
		ss = 1;
	}
private:
	int ss;
};

void inputval(const Teacher& t, const Student& s)
{
	cout << t.x << endl;
	cout << s.ss << endl;
}

int main()
{
	Teacher t(16);
	Student s;

	inputval(t, s);

	return 0;
}

结果如下图所示子类的友元函数找不到其父类的私有与保护成员 

 3.继承与静态成员

父类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例。

 如下代码所示

#include<iostream>
using namespace std;

class Teacher
{
public:
	string _name;
	static int _count;
};
int Teacher::_count = 0;
class Student : public Teacher
{
protected:
	int _stuNum;
};
int main()
{
	Teacher t;
	Student s;
	// 这⾥的运⾏结果可以看到⾮静态成员_name的地址是不⼀样的 
	// 说明⼦类继承下来了,⽗⼦类对象各有⼀份 
	cout << &t._name << endl;
	cout << &s._name << endl;
	// 这⾥的运⾏结果可以看到静态成员_count的地址是⼀样的 
	// 说明⼦类和⽗类共⽤同⼀份静态成员 
	cout << &t._count << endl;
	cout << &s._count << endl;
	// 公有的情况下,⽗⼦类指定类域都可以访问静态成员 
	cout << Teacher::_count << endl;
	cout << Student::_count << endl;
	return 0;
}

运行结果如下

我们可以看到子类对象中的_name与父类对象中的_name地址不同,而子类对象与父类对象的_count地址是相同的。说明父类对象与子类对象共用一个静态成员

4.多继承及其菱形继承问题

(1). 继承模型

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

 如下图

 

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承,多继承对象在内存中的模型是  先继承的父类在前面,后继承的父类在后面,子类成员在最后面

 如下图

在内存中的分布如下

菱形继承:菱形继承是多继承的一种特殊情况。菱形继承的问题,从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题,在Assistant的对象中Person成员会有两份。支持多继承就一定会有菱形继承,实践中我们尽量不设计出菱形继承这样的模型

 

 代码演示

#include<iostream>
#include<string>

using namespace std; 

class Person
{
public:
	string _name; // 姓名 
};
class Student : public Person
{
protected:
	int _num; 
};
class Teacher : public Person
{
protected:
	int _id; 
};
class headmaster : public Student, public Teacher
{
protected:
	string _Course; 
};

上述代码即为一个菱形继承

int main()
{
	// 编译报错: 对“_name”的访问不明确 
	headmaster a;
	a._name = "peter";
	return 0;
}

这样调用编译会报错

编译器不知道调用的是哪个父类中的_name

我们可以通过显式指定访问那个父类的成员来解决二义性的问题,但是无法解决数据冗余的问题

int main()
{
    //
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
	return 0;
}
(2). 虚继承

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承即可解决问题。但是要注意,虚拟继承不要在其他地方去使用

using namespace std;

class Person
{
public:
	string _name; // 姓名 
};
class Student : virtual public Person
{
protected:
	int _num;
};
class Teacher : virtual public Person
{
protected:
	int _id;
};
class headmaster : public Student, public Teacher
{
protected:
	string _Course;
};

int main()
{
	headmaster h;
	h._name = "lisi";
	cout << h._name << endl;
	h.Student::_name = "l";
	cout << h._name << endl;
	h.Teacher::_name = "s";
	cout << h._name << endl;

	cout << &h._name << endl;
	cout << &h.Student::_name << endl;
	cout << &h.Teacher::_name << endl;

	return 0;
}

输出结果如下

 可以看到这三个在内存中用了一个地址空间,这样解决了二义性和数据冗余的问题

(2.1)虚继承解决数据冗余和二义性的原理

如下代码

#include<iostream>
#include<string>

using namespace std;

class Person
{
public:
	int a;
};
class Student : virtual public Person
{
public:
	int _num;
};
class Teacher : virtual public Person
{
public:
	int _id;
};
class headmaster : public Student, public Teacher
{
public:
	int test;
};

int main()
{
	headmaster h;

	h.Student::a = 1;
	h.Teacher::a = 2;

	h._num = 3;
	h._id = 4;
	h.test = 5;

	return 0;
}

 在内存中如下所示

我们上面提到过,多继承中,先继承的父类在前面后继承的父类在后面,子类成员放在最后面。我们这里可以看出headmaster对象中将Person放到了对象组成的最下面,这个Person同时属于Student和Teacher,那Student和Teacher如何去找到公共的Person呢?

这里是通过它们的两个指针指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表虚基表中存的偏移量。通过偏移量可以找到Person

 内存2是p1指向的地址,内存3是p2指向的地址它们下面指针的指向是相同的

Teacher和Student自己定义的对象也可以通过这样来找到Person

如下

int main()
{
	Teacher t;
	Student s;

	t.a = 1;
	cout << t.a << endl;

	s.a = 2;
	cout << t.a << endl;
	cout << s.a << endl;
	return 0;
}

结果为

(3). 多继承中指针偏移问题

关于下面程序说法正确的是

A: p1==p2==p3  B: p1<p2<p3  C: P1==p3!=p2  D: p1!=p2!=p3

class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
int main()
{
 Derive d;
 Base1* p1 = &d;
 Base2* p2 = &d;
 Derive* p3 = &d;
 
 return 0;
}

先继承的父类在前面,后继承的父类在后面,子类成员在最后面,所以p1与p3指向相同

选C

(4). IO库中的菱形虚拟继承

template<class CharT, class Traits = std::char_traits<CharT>>
class basic_ostream : virtual public std::basic_ios<CharT, Traits>
{};
template<class CharT, class Traits = std::char_traits<CharT>>
class basic_istream : virtual public std::basic_ios<CharT, Traits>
{};

5. 继承和组合

1. public继承是一种is-a的关系。也就是说每个子类对象都是一个父类对象

2. 组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象

3. 继承允许你根据父类的实现来定义子类的实现。这种通过生成子类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,父类的内部细节对子类可见。继承一定程度破坏了父类的封装,父类的改变,对子类有很大影响。子类和父类之间的依赖关系很强,耦合度高

4. 对象组合时类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装

5. 优先使用组合,而不是继承。实际尽量多去用组合,组合的耦合度低,代码维护性好。不过也不那么绝对,类之间的关系适合继承(is-a)那就用继承,另外要实现多态也必须要继承。类之间的关系既适合用继承(is-a)也适合用组合(has-a),那就用组合

6. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂,性能也会由一些损失,所以最好不要设计出菱形继承。多继承可以认为是C++的缺陷之一,后来的一些编程语言都没有多继承比如Java

例如汽车类(car) 和 轮胎类(tire) 适合使用组合方式实现,动物类可以作为狗类的父类(继承思想)


这篇就到这里啦(づ ̄3 ̄)づ╭❤~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2109829.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内蒙古众壹集团:引领蒙东财税服务行业,成就企业发展新高度

内蒙古众壹企业管理集团有限公司自2019年成立以来&#xff0c;凭借卓越的服务和专业的团队&#xff0c;迅速成长为蒙东地区财税服务行业的先锋企业。 公司在成立初期&#xff0c;通过加盟慧算账平台&#xff0c;快速进入市场&#xff0c;并设立了多个分公司&#xff0c;逐步扩展…

Daily2:字体描边

有一个小的需求,需要对字体进行描边,一开始理解错了需求,以为要对字体镂空处理,然后尝试了许多做错了许多 后来发现是一个简单的描边处理,直接chatgpt就可以得出来一个简单的实现代码, class BorderTextView JvmOverloads constructor(context: Context, attrs: AttributeSet?…

读懂以太坊源码(3)-详细解析genesis.json

要想搞懂以太坊的源代码逻辑&#xff0c;必须要了解以太坊创世区块配置文件(genesis.json)的结构&#xff0c;以及每个配置参数的意义&#xff0c;创世配置文件&#xff0c;主要作用是设置链的ID&#xff0c;指定以太坊网络中硬分叉发生的区块高度&#xff0c;以及初始ETH数量的…

【系统分析师】-软件测试

目录 1、测试的类型 1.1、动态测试 1.1.1、黑盒法 1.1.2、白盒法 1.1.3、灰盒法 1.2、静态测试 2、测试阶段 2.1、单元测试 2.2、集成测试 2.3、确认测试 2.4、系统测试 3、性能测试 3.1、性能测试的目的 3.2、性能测试的类型 3.3、性能测试的步骤 5、测试设计…

【操作系统存储篇】Linux文件基本操作

目录 一、Linux目录 二、Linux文件的常用操作 三、Linux文件类型 一、Linux目录 Linux有很多目录&#xff0c;Linux一切皆是文件&#xff0c;包括进程、设备等。 相对路径&#xff1a;相对于当前的操作目录&#xff0c;文件位于哪个目录。 绝对路径 &#xff1a;从根目录开…

面对AI时代快车,你没必要跟车赛跑,而是应该先去考个驾照!

在当今人工智能领域&#xff0c;代码生成和编辑工具成为了开发者们手中的利器。昨天&#xff0c;零一万物公司再次展示了他们在开源社区中的领导地位&#xff0c;开源了 Yi-Coder 系列编程助手模型&#xff0c;这一举措不仅标志着该公司继今年5月开源Yi-1.5系列模型后的又一重大…

智能提醒助理系列-服务号静默登录

本系列文章记录“智能提醒助理”wx公众号 建设历程&#xff0c;记录实践经验、巩固知识点、锻炼总结能力。 本文介绍&#xff0c;如何让用户进入公众号之后就锁定用户&#xff0c;使用既注册&#xff0c;进入既可使用功能&#xff0c;去掉繁琐的登录认证流程。 一、需求出发点 …

告别单调,Xmind思维导图之后还有这三款神器,让学习工作更愉快

这年头信息量爆炸&#xff0c;我们得想办法把事情想清楚、把活儿排排好、学点新玩意儿。思维导图这东西&#xff0c;因为它画出来一目了然&#xff0c;用起来也简单&#xff0c;所以特别受学生们和上班的人的欢迎。在这么多画思维导图的软件里&#xff0c;Xmind因为功能全、界面…

02【SQL sever 2005数据库安装教程】

一、安装须知 1.安装数据库版本&#xff1a;SQL sever 2005 2.适用系统&#xff08;目前发现&#xff09;&#xff1a;Windows server 2008 R2 3.安装程序目录&#xff1a;SQL2005\SQL Server x64\Servers\setup.exe 二、安装步骤 1&#xff0e;双击setup.exe&#xff0c;以…

c++编程(25)——unordered_map模拟实现

欢迎来到博主的专栏&#xff1a;c编程 博主ID&#xff1a;代码小豪 文章目录 unorder_map的底层insert迭代器成员访问函数operatoroperator-\- unordered_map是STL中的关联式容器之一&#xff0c;与常规的map有两点不同 &#xff08;1&#xff09;unordered是无序的意思&#x…

AI与我同创诗:尝试让ai(智谱清言)参与我的诗创活动

ai伴学越久&#xff0c;契合度愈高&#xff0c;“泛滥”之诗情&#xff0c;幸得学伴共雕琢。让ai伴学久了&#xff0c;不知觉的&#xff0c;写诗也让ai帮衬了。此文收录“我共ai”的自创文稿&#xff0c;亦可作“ai诗集”。&#x1f60b; (笔记模板由python脚本于2024年09月03日…

企业微信中嵌套的h5应用调用微信扫码功能

企业微信官方文档 1.登录企业微信后台,管理员可操作,打开应用配置应用可信域名(必须配置,否则无法调用jsapi,可信域名必须有ICP备案且在管理端验证域名归属) 配置部署后的前台域名地址 配置可信域名,部署后的服务器域名(需备案认证) 当域名权限不够时需下载文件效验,将文件放…

网站开发:HTML + CSS - CSS选择器

1. 前言 CSS&#xff08;Cascading Style Sheets&#xff0c;层叠样式表&#xff09;是一种用于控制 HTML 文档样式和布局的语言。它为 Web 页面提供了许多功能&#xff0c;使开发者能够创建美观且功能丰富的用户界面。 提供了丰富的功能来控制网页的外观和布局&#xff0c;增…

掌握SQLAlchemy:Python数据库的魔法师

文章目录 掌握SQLAlchemy&#xff1a;Python数据库的魔法师背景&#xff1a;为什么选择SQLAlchemy&#xff1f;SQLAlchemy是什么&#xff1f;如何安装SQLAlchemy&#xff1f;五个简单的库函数使用方法1. 创建引擎2. 定义模型3. 创建会话4. 添加数据5. 查询数据 场景应用1. 多表…

大模型构建合作性的Agent,多代理框架MetaGpt

大模型构建合作性的Agent,多代理框架MetaGpt 前言 MetaGPT 框架将标准的操作程序(SOP)与基于大模型的多智能体相结合,使用标准操作程序来编码提示,确保协调结构化和模块化输出。 MetaGPT 允许 Agent 在类似流水线的范式中扮演多中角色,通过结构化的 Agent 协作和强化领…

Android Studio 加载多个FLutter项目

按顺序操作即可 选择工程 选择Modules, 导入 module 选中创建module 选择要导入的目录&#xff0c;只选择主目录&#xff0c;下面的文件不要选 添加完成&#xff0c;点击ok后&#xff0c;会进行导入 最终导入成功

三星称霸全球市场,但它在中国市场再受打击,将进一步收缩业务

韩国媒体报道指三星已计划进一步收缩中国业务&#xff0c;将中国的销售和生产部门人员减少一部分&#xff0c;其中销售人员计划减少8%左右&#xff0c;显示出它在中国市场继续面临打击&#xff0c;对于这家在全球市场领先地位仍然稳固的企业来说&#xff0c;它在中国市场无疑又…

OpenCV结构分析与形状描述符(8)点集凸包计算函数convexHull()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 查找一个点集的凸包。 函数 cv::convexHull 使用斯克拉斯基算法&#xff08;Sklansky’s algorithm&#xff09;来查找一个二维点集的凸包&#…

Wyn 商业智能V8.0 新版本来袭,解锁“智造”的无限可能

Wyn商业智能V8.0 版本全新发布&#xff0c;聚焦制造业数字化升级痛点&#xff0c;深度赋能制造业数字化转型升级之路&#xff0c;从无缝集成物联网海量数据&#xff0c;到构建可视化实时分析、监控与预警大屏&#xff0c;全面打通生产制造全生命周期的数据脉络&#xff0c;为您…

免费OCR 文字识别工具

免费&#xff1a;本项目所有代码开源&#xff0c;完全免费。 方便&#xff1a;解压即用&#xff0c;离线运行&#xff0c;无需网络。 高效&#xff1a;自带高效率的离线OCR引擎&#xff0c;内置多种语言识别库。 灵活&#xff1a;支持命令行、HTTP接口等外部调用方式。 功能…