c++继承 和 多态

继承的好处,可以减少代码的重复
class A : 继承方式 B;
A类为子类,B类为父类
子类的成员分别由从父类继承来的成员 和自己本身的成员构成。

继承方式有public,protect,private 三种
首先 不管是哪种继承方式,子类都无法继承父类的私有成员,并非没有继承,而是私有成员被隐藏。
如果是公有继承,则子类直接继承父类中除了私有的成员,并且不做任何变化
如果是保护继承,父类中除了私有的成员在子类中全部变成保护权限(保护权限 类外不可以访问)
如果是私有继承,则全部变成私有权限

在这里插入图片描述

继承中的对象模型
cl /d1 reportSingleClassLayout类名 文件名.cpp 可以在命令行中查看类的结构

子类继承父类后,当创建子类对象,也会调用父类的构造函数
继承中的构造和析构的顺序如下,先构成父类再构成子类,析构顺序反过来

继承同名成员处理方式
如果通过子类对象访问父类的同名成员,需要加入作用域 s.father::age

多继承语法
允许一个类继承多个类
语法: class 子类: 继承方式 父类1, 继承方式 父类2,。。。
多继承可能引发父类中有同名成员的出现, 因此要室友作用域加以区分

菱形继承
两个子类同时继承一个父类,又有某个子类同时继承两个子类,这种继承被称为菱形继承
比如 动物 - 羊,驼-羊驼, 因为羊驼继承了羊和驼,那么数据会产生二义性
利用虚继承解决菱形继承问题, 在继承类型前加上关键字virtual 变成虚继承

在这里插入图片描述

多态

多态分为静态多态和动态多态
静态多态:函数重载和运算符重载都是静态多态,复用函数名
动态多态: 子函数和虚函数实现运行时多态
区别:
静态多态的函数地址早绑定,即编译阶段确定函数地址
动态多态的函数地址晚绑定,运行阶段才确定函数地址


class Animal {
public:
	virtual void speak() {
		cout << "animal speak chinese" << endl;
	}
};

class Cat : public Animal {
public:
	void speak() {
		cout << "cat speak meow" << endl;
	}
};

void doSpeak(Animal* animal) {  // 在c++中,父类的指针可以直接指向子类
	animal->speak();  
}


int main() {
	Cat c;
	c.speak();
	doSpeak(&c);  //执行的是动物在说话,因为地址早绑定了 如果想要地址晚绑定 在说话函数前加上virtual
}

动态多态满足条件
1.有继承关系
2.子类要重写父类的虚函数

多态原理
在这里插入图片描述
类中含有virtual 成员函数,即便不再含有其他变量和函数,该类仍然占有4字节(空类只占1字节),原因是virtual 成员函数会给类添加一个virtual function ptr, 该指针指向一个列表,里面记录着虚函数的地址,当子类继承时,也会继承该指针,并指向子类的虚函数的地址,而子类的虚函数和父类的不一样,因此,当出现Animal & animal = cat 时,此时虚函数列表为cat的列表,故而animal.speak访问的是cat的speak。

在多态中,通常父类中虚函数的实现是毫无意义的,主要调用子类重写的内容,因此可以将虚函数改成纯虚函数

纯虚函数写法:
virtual 返回值类型 函数名(参数列表) = 0;
当类中有了虚函数,这个类也称为抽象类
抽象类的特点:无法实例化对象,且子类必须重写抽象类的纯虚函数,否则子类也是抽象类

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在析构时无法调用到子类的析构代码
解决方式:将父类的析构函数改成虚析构和纯虚析构
虚析构和纯虚析构的共性:可以解决父类指针释放子类对象,都需要具体的函数实现
区别: 如果是纯虚析构,那么该类属于抽象类,无法实例化对象。

虚析构和纯虚析构都需要声明 和代码实现, 但如果子类中没有堆区数据,可以不写虚析构或者纯虚析构
示例代码:

#include "base.h"

class Animal {
public:
	Animal() {
		cout << "Animal 构造函数" << endl;
	}
	virtual ~Animal() = 0;
	virtual void speak() = 0;
};
Animal :: ~Animal() {
	cout << "Animal 析构" << endl;


}
class Cat:public Animal {
public:
	string* name;
	Cat(string nom) {
		cout << "Cat 构造函数" << endl;
		name = new string(nom);
	}
	void speak() {
		cout << *this->name << " is speaking" << endl;
	}

	~Cat() {
		cout << "Cat 析构函数" << endl;
		if (this->name != NULL) {
			delete  name;
			name = NULL;
		}
	}

};

int main() {
	Animal* animal = new Cat("tom");
	animal->speak();
	delete animal;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值