C++ | 【03】类的【继承、多态、复合、委托】

一、继承

1、概述

为了减少代码重复内容。

// 父类
class man{

}

// 子类
class d :public man{
}
2、访问控制
  • public:不可访问私有属性;
  • protected:把public都变成protected
  • private:把publicprotected变成可访问的。

子类会继承父类的私有属性,但被编译器隐藏了,访问不到私有成员。

3、继承中的构造和析构

子类不会继承父类的构造函数和析构

  • 子类创建对象时,先调用父类的构造,在调用自身构造;析构的顺序与之相反;
  • 若父类中没有合适默认构造,子类可以利用初始化列表的方式显示的调用父类的其他构造。
4、同名处理
  • 子类和父类中同名不会覆盖。
  • 若在子类对象要调用父类的属性时,只需在属性前增加作用域即可;
5、多继承
  • 同时继承多个基类。
  • 会造成二义性,调用时在属性前增加作用域。
6、菱形继承和虚继承
  • 两个派生类继承同一个基类而又有某个类同时继承者两个派生类这种继承被称为菱形继承,或者钻石型继承

在这里插入图片描述

虚继承:解决菱形方案,操作的是共享的一份数据。

  • vbptr 虚基类指针;
  • 指向一张虚基类表;
  • 通过表找到偏移量;
  • 找到共有的变量。

二、复合【composition】

一个类中存在另一个类作为数据成员;

#include<iostream>

using namespace std;

class queue {

public:
	
private:	
	deque m_deque;
};

class deque {
	
public:
	
private:
	//.... 
};

在这里插入图片描述

此时,deque作为queue的数据成员,当queue对象被创建时,会先调用deque的构造函数,在调用自身的;而析构函数的调用顺序则相反

1、继承与复合构造函数调用顺序?
#include<iostream>

using namespace std;

class A {
public:
	A() {
		cout << "A" << endl;
	}
	~A() {
		cout << "~A" << endl;
	}
};

class C {
public:
	C() {
		cout << "C" << endl;
	}
	~C() {
		cout << "~C" << endl;
	}
};

class B : public C{
public:
	B() {
		cout << "B" << endl;
	}
	~B() {
		cout << "~B" << endl;
	}
private:
	A a;
};

int main() {
	B b;
	
	return 0;
}

在这里插入图片描述

三、委托【delegation】

一个类作为另一个类的数据成员,且该成员为指针类型;

在这里插入图片描述

此时,queue对象创建,deque不会创建;

四、多态

1、基本概念
  • 多态是面向对象程序设计语言中数据抽象和继承之外的第三个基本特征;
  • 多态性(polymorphism)提供接口与具体实现之间的另一层隔离,从而将" what" 和"how"分离开来。多态性改喜了代码的可读性和组织性,同时也使创建的程序具有可扩展性
  • C++支持编译时多态(静态多态)和运行时多态(动态多态) ,运算符重载和函数重载就是编译时多态,而派生类虚函数实现运行时多态;
  • 静态多态和动态多态的区别就是函数地址是早绑定(静态联编)还是晚绑定(动态联编)。如果函数的调用,在编译阶段就可以确定函数的调用地址,并产生代码,就是静态多态(编译时多态) ,就是说地址是早绑定的。而如果函数的调用地址不能编译不能在编译期间确定,而需要在运行时才能决定,这这就属于晚绑定(动态多态,运行时多态)。
2、使用
  • 静态联编:程序的匹配、连接在编译阶段实现即确定地址,重载函数使用静态联编;
  • 动态联编:程序联编推迟到运行时进行;
#include<iostream>
using namespace std;


/*
	当没有在spreak前加上virtual时函数在编译阶段即确定好了地址;
	若要调用猫的,则在该函数前加上virtual。
*/
class  Animal
{
public:
	virtual void speak(){
		cout << "我是动物" << endl;
	}
private:

};

class Cat :public Animal
{
public:
	void speak(){
		cout << "我是小猫" << endl;
	}
private:

};

void doSpeak(Animal & animal){
	animal.speak();
}


void test(){
	Cat cat;
	doSpeak(cat);
}


int main(){

	test();

	system("pause");
	return 0;
}

可扩展性

  • 遵循开闭原则;对拓展开放,对修改关闭。
  • 拓展只需继承父类,对其方法进行重写即可。
3、纯虚函数【抽象类】
  • 虚函数格式:virtual void 函数名() = 0;
  • 若父类中有了纯虚函数,子类继承父类就必须实现纯虚函数。
  • 若父类中有纯虚函数,则这个父类就无法实例化对象。
4、虚函数
  • 父类中有实现,子类可选择实现或者不实现
  • 可能出现在其中有子类不需要用到该虚函数,而有些子类需要用到且重写该虚函数时,可将其定义为虚函数且为空函数
5、虚析构
  • 普通析构函数不会调用子类析构;
  • 在多态实现中,在析构前面添加virtual ,变为虚析构。

纯虚析

  • 写法与纯虚函数一样,在类内声明,类外实现
  • 父类析构函数必须为virtual 否则出现undefined behavior
  • 该类也是抽象类,不可实例化。
6、向上向下类型转换
6.1 基类转派生类
  • 向下类型转换不安全;
6.2 派生类转基类
  • 向上类型转换安全;

若发生多态则总是安全的。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jxiepc

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

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

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

打赏作者

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

抵扣说明:

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

余额充值