c++继承

本文详细阐述了面向对象编程中的继承机制,包括不同访问权限的控制、struct和class的继承区别、赋值兼容性、作用域规则、默认成员函数以及单继承与多继承(尤其是菱形继承和虚继承)的概念。强调了组合与继承的使用策略,提倡低耦合和高内聚的设计原则。
摘要由CSDN通过智能技术生成

目录

一、继承格式

二、子类对父类的权限

​1、private

2、protect

3、public

总结:

三、 struct和class继承区别

四、赋值兼容

五、继承中的作用域

1、子父类的作用域是独立的

2、子父有相同成员:隐藏

3、怎么访问父类?

六、继承类的默认成员函数

1、构造

2、拷贝构造

3、赋值重载

4、析构函数

5、静态成员的继承

总结

七、单继承和多继承

1、菱形继承问题

2、如何解决菱形继承—虚继承

3、总结

八、组合与继承(优先使用组合)

1、低耦合和高内聚


一、继承格式


class son public: father

二、子类对父类的权限


1、private

子类不可直接访问
但是子类可以间接使用
例如调父类的get函数
 

2、protect

在类外面不能访问
但是子类可以访问

3、public

子类和父类的权限比较,那个小就取那个
例如子类为public方式继承,但是父类的A函数为protect
那么,最终子类对A函数的权限就是protect

总结:

父类的private子类都不能用,其余public和protect要和子类的继承方式比较,那个小取那个

三、 struct和class继承区别

struct默认继承方式和访问限定符都是共有
class默认继承方式和访问限定符都是私有
 

四、赋值兼容

类型相近的类型可以互相转换
例如,int和double可以互相转换
同理,依照上述例子,子类对象也可以赋值给父类对象
子类在public继承的情况下,子类可以转化为父类类型
可以理解为每个子类对象都是一个特殊的父类对象
怎么给?
共有的部分赋值给父类
但是子类独有的部分不会给到父类对象
这个过程叫做切割/切片
期间不产生临时变量
但是,父不能给子
很好理解
因为有些东西子类有,但是父类没有,无法赋值
 

五、继承中的作用域

1、子父类的作用域是独立的

因此不构成函数重载

2、子父有相同成员:隐藏

访问的是子类的,就近原则
子类会把父类的同名成员屏蔽,这种情况叫做隐藏

3、怎么访问父类?

指定作用域访问
子类对象指定访问父类域内的成员

son.father::father_func();

如果子类直接访问父类的成员,而没有指定类域,就会导致编译错误
因为子类域内找不到,但是你又没有告诉我要到那个域去找,所以我不知道,所以报错

六、继承类的默认成员函数

1、构造

子类的默认构造怎么处理父类?
子类分成了两个部分:
一个是父类,将父类看成一个类
一个是子类本身,内置类型不处理,自定义类型调用对应的构造

也就是说,如果子类在初始化的时候
会调用父类的默认构造
剩下的部分要调用子类自己的默认构造

但是,如果父类没有自己的默认构造,子类也会报错
相当于,子类的一个自定义类型没有其对应的默认构造
派生类一定会去调用父类的构造,因为要继承
这一块很简单,父类就是子类的一个自定义对象

2、拷贝构造

和构造相同
将父类视为一个简单的自定义类型
仅此而已
因此不需要自己写
但是如果要深拷贝,就要自己写

3、赋值重载

例如是operator=运算符重载
子类和父类都有一个,要注意构成隐藏
如果要调用父类的operator=需要指定域访问 

4、析构函数

如果是自定义的析构,也会构成隐藏
但是,子类析构结束后,会自动调用父类析构函数
但是还是可以显式指定域访问父类析构
虽然析构函数看起来名字不相同,属于各自的类

5、静态成员的继承

父类的静态成员只有一份
所有的子类都共用这一个静态成员

总结

原则:
构造保证先父后子
析构保证先子后父
常见复用的体现:
1)函数逻辑的复用
2)模板的复用
3)继承的复用


七、单继承和多继承

单继承:只有一个直接父类
多继承:两个及以上的直接父类

1、菱形继承问题

什么叫做菱形问题?
这个问题是从继承区分单继承和多继承这个特性上衍生出来的
例如下面这个图:

那么,在这种情况下,这个D就会继承有Base的成员
这就是菱形问题:
a、数据冗余
b、数据二义性,访问错误,因为不知道到底访问的是谁的

2、如何解决菱形继承—虚继承

用关键字virtual解决(其实已经涉及到多态了)
格式:

virtual void func() {}

看一个具体例子:
 

#include<iostream>
using namespace std;

//grand
class grand
{
public:
	virtual void func()//父类必须加vitual,加了vitual关键字,就叫做虚函数
	{
		cout << "grand->func" << endl;
	}

private:
	int _grand;
};


//父类(基类)
class father1 : public grand
{
public:
	virtual void func()//父类必须加vitual,加了vitual关键字,就叫做虚函数
	{
		cout << "father1->func" << endl;
	}

private:
	int _father1;
};

//父类(基类)
class father2 : public grand
{
public:
	virtual void func()//父类必须加vitual,加了vitual关键字,就叫做虚函数
	{
		cout << "father2->func" << endl;
	}

private:
	int _father2;
};


/子、父类虚函数,函数名相等,返回值相同,参数相同,构成重写

//子类(派生类)
class son : public father1 ,public father2
{
public:
	virtual void func() //子类可以不加vitual,但是建议加上
	{
		cout << "son->func" << endl;
	}

private:
	int _son;
};



int main()
{
	grand g;
	father1 f1;
	father2 f2;
	son s;

	g.func();
	f1.func();
	f2.func();
	s.func();



}

这个可以重点理解一下
底层如何解决?可以看另一篇博主的文章


3、总结

在实践中,可以设计多继承
但是不要设计出菱形继承的结构
(iostream的io流就是使用菱形继承)

菱形继承是在2.0版本出现的
到了3.0才出现的
既然会出现歧义问题
为什么不删掉呢?
因为要兼容以前的历史版本,即向前兼容
所以,只有解决,不能删除
所以,尽管付出了解决的代价,即大大的增加的了设计的复杂度
但是,这是必须要付出的代价
这也是历史发展的包袱,历史发展的惯性,历史发展的代价

八、组合与继承(优先使用组合)

1、低耦合和高内聚

低耦合就是模块之间、类之间的联系低,互相影响程度低
例如说,人类和石头类,联系程度低,二者的组合就是低耦合
但是也是分使用场景

高内聚就是一个类内部的各种数据要尽可能的关联度紧密
例如,一个人对象的类,对于发量、交往对象的信息数据来说,大多数场景下,不是那么必要
所以,不是高内聚
而姓名、性别、年龄等信息就和人这个类关系紧密
继承耦合度高、组合耦合度低
耦合度低维护性更好
这个很好理解,因为组合相对于继承来说,类之间的紧密程度更低,关联影响更低
所以耦合度就低,耦合度低,容错率就高,更加稳定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二十5画生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值