c++继承(二)

一、友元函数的继承

友元函数不能被继承,就像爸爸的朋友不是你的朋友,如果要有友元函数,在子类重新定义一个。

二、静态成员的继承

静态成员的继承仍然是那个成员,普通成员的继承是不同的。

父类的静态成员属于当前类,也属于当前类的派生类。

三、单继承与多继承

1、单继承

一个子类只有一个直接父类

2、多继承

一个子类有两个或以上的直接父类

四、菱形继承

1、理解

如图 Assistant 就是一个菱形继承,显然如果不做特殊处理 Assistant 里面会有两份 Person 类中的成员,这就导致了数据冗余和二义性。

解决办法就是在菱形继承中父类被继承两次的地方进行虚继承virtual

被继承的 Person 类是虚基类。

2、剖析

(1)未用 virtual

cpp 复制代码
class A
{
public:
	int _a;
};

class B : public A
//class B : virtual public A
{
public:
	int _b;
	int _b1;
	int _b2;

};

class C : public A
//class C : virtual public A
{
public:
	int _c;
};
class D : public C, public B
{
public:
	int _d;
};

int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;

	//d._a = 0;
	d._b = 3;
	d._c = 4;
	d._d = 5;
}

上图就是典型的菱形继承且没有解决,这样在D类中我们发现两个_a是不一样的,要想使用必须指定类域,这就是数据冗余和二义性。

内存级理解:

明显的看到内存中D对象中的B,C存有不同的_a,这是不被允许的。

(2)用 virtual

cpp 复制代码
class A
{
public:
	int _a;
};

//class B : public A
class B : virtual public A
{
public:
	int _b;
	int _b1;
	int _b2;

};

//class C : public A
class C : virtual public A
{
public:
	int _c;
};
class D : public C, public B
{
public:
	int _d;
};

int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;

	d._a = 0;
	d._b = 3;
	d._c = 4;
	d._d = 5;
}

上图我们用虚继承解决了问题,此时类D中的_a是同一个_a,所以最后_a值应该是0

内存级理解:

首先B,C类中不再存有_a的值,而是用一个地址代替了,_a的值被放到了D对象的内存最下面,地址所指向的内容存放的是_a的值0,还有十六进制的偏移量,用来找到原内存中_a的存储位置。

这样就解决了问题。

(3)总结

a、存地址的偏移量是为了方便找到数据,方便切片。

b、虚继承的解决原理:把原来存父类成员的地方存地址,通过地址和偏移量找到唯一的父类成员。实现原来有几个类继承就有几份父类成员的数据冗余变成现在存几个地址和偏移量来找到内存中唯一的一份父类成员,解决了数据冗余和二义性。

c、对象的存储按声明顺序存储。

五、继承和组合

我们发现继承和组合都能复用之前的类,两者是相似的,在实践中两者也会一起使用。

cpp 复制代码
//父类
class A {};

//继承
class B : public A
{
    //直接继承,直接使用
};

//组合
class C
{
private:
    A _aa;  //创建 A 对象,使用成员及方法
}

|----|-------------------------------------------------------------------------|
| 方式 | 备注 |
| 继承 | is_a关系,白箱复用(在继承方式中,基类的 内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响)耦合度高。 |
| 组合 | has_a关系,黑箱复用(为对象的内部细节是不可见的)耦合度低。 |

优先使用组合,组合的耦合度低,代码维护性好。

相关推荐
郝学胜-神的一滴18 分钟前
【技术实战】500G单行大文件读取难题破解!生成器+自定义函数最优方案解析
开发语言·python·程序人生·面试
愤豆22 分钟前
02-Java语言核心-语法特性-注解体系详解
java·开发语言·python
不想写代码的星星28 分钟前
SFINAE 的演进:从替换失败不是错误,到 Concepts 的优雅
c++
是翔仔呐37 分钟前
第13章 SPI通信协议全解:底层时序、4种工作模式与W25Qxx Flash芯片读写实战
c语言·开发语言·stm32·单片机·嵌入式硬件·学习·gitee
2401_878530211 小时前
自定义内存布局控制
开发语言·c++·算法
wjs20241 小时前
SQLite 子查询
开发语言
AndrewMe82111 小时前
detailed-docx:一个能保住格式的 Word 文档操作库
开发语言·python·word
IT方大同1 小时前
RT_thread(RTOS实时操作系统)线程的创建与切换
c语言·开发语言·嵌入式硬件
智算菩萨1 小时前
【OpenGL】6 真实感光照渲染实战:Phong模型、材质系统与PBR基础
开发语言·python·游戏引擎·游戏程序·pygame·材质·opengl
jinanwuhuaguo1 小时前
OpenClaw深度沟通渠道-全景深度解构
大数据·开发语言·人工智能·openclaw