爱上c++的第六天(核心课程):继承和多态

本文详细探讨了C++中的继承概念,包括其好处、语法和不同继承方式,强调了如何处理同名成员。同时,文章深入介绍了多态的原理,包括静态和动态多态的区别,以及如何通过虚函数实现运行时多态。文中还通过实例展示了多态在实际编程中的应用,如动态绑定和类型转换。最后,讨论了虚析构函数和纯虚函数在抽象类中的作用,以及它们在资源管理中的重要性。
摘要由CSDN通过智能技术生成

你的c++学习路上明灯

你的c++学习路上明灯_哔哩哔哩_bilibiliicon-default.png?t=M276https://www.bilibili.com/video/BV16P4y1M7cX?p=10 

目录

继承:

一,基本介绍:

1,好处:

2,语法:

3,

二,详细的使用

1.继承方式:

2.注意我上面的那句话,无法访问到,这就说明了,父类中私有成员也会被子类继承,只是由编译器隐藏后访问不到了。

3.继承中构造函数和析构函数的调用顺序

4.同名成员处理

5.同名静态成员处理方式和非静态处理方式一样,只不过有两种访问方式(通过对象,通过类名)

6.多继承语法

7.菱形继承

多态:

一,基本介绍

二,详细的使用

1.动态多态满足条件:

2.使用动态多态:

3.重写:函数返回值类型    函数名,参数列表完全一致称为重写。

4.多态的优点

5.纯虚函数

6.抽象类特点:

7.虚析构和纯虚析构的共性

8.实例

学习c++的人一定知道的就是c++是面向对象设计的,而面对对象的三大特性就是封装,继承和多态,我们在刚开始学习的时候就已经学过封装了,今天我们主要来学习一下多态和继承。

类的定义的出现,是具有革命性意义的,而继承和多态就是让c++变得更为方便的一笔。

继承:

一,基本介绍:

1,好处:

减少重复代码。

2,语法:

class 子类(派生类) : 继承方式 父类(基类)

3,

派生类中的成员包含两大部分:

一部分是从基类中继承来的东西,另一部分是自己增加的部分

从基类继承过来的表现其共性,而新增成员体现其个性

二,详细的使用

1.继承方式:


1)公共继承:父类权限不变;

2)保护继承:全部变成保护权限

3)私有继承:全部变为私有权限

上述三种继承方式都无法访问到分类的私有权限下的内容,但是可以访问到保护权限下的。

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

//普通实现页面
//需要大量的重复代码,冗余。
//class Java {
//public:
//
//	void header() {
//		cout << "首页,公开课。。。。(公共头部)" << endl;
//	}
//	void footer() {
//		cout << "帮助中心,交流合作。。。(公共底部)" << endl;
//	}
//	void content() {
//		cout << "Java学科视频" << endl;
//	}
//
//};
//
//class Python {
//public:
//
//	void header() {
//		cout << "首页,公开课。。。。(公共头部)" << endl;
//	}
//	void footer() {
//		cout << "帮助中心,交流合作。。。(公共底部)" << endl;
//	}
//	void content() {
//		cout << "Python学科视频" << endl;
//	}
//
//};

//class Cpp {
//public:
//
//	void header() {
//		cout << "首页,公开课。。。。(公共头部)" << endl;
//	}
//	void footer() {
//		cout << "帮助中心,交流合作。。。(公共底部)" << endl;
//	}
//	void content() {
//		cout << "Cpp学科视频" << endl;
//	}
//
//};

//继承页面
// 重复的部分只用写一次。

//公共页面类
class basepage {
public:
	void header() {
		cout << "首页,公开课。。。。(公共头部)" << endl;
	}
	void footer() {
		cout << "帮助中心,交流合作。。。(公共底部)" << endl;
	}

};

class Java : public basepage {
public:
	void content() {
		cout << "Java学科视频" << endl;
	}
};

class Python : public basepage {
public:
	void content() {
		cout << "Python学科视频" << endl;
	}

};

class Cpp :public basepage {
public:
	void content() {
		cout << "Cpp学科视频" << endl;
	}
};

void test1() {
	cout << "Java下载视频如下:" << endl;
	Java ja;
	ja.content();
	ja.footer();
	ja.header();
	cout << "------------------" << endl;

	cout << "Python下载视频如下:" << endl;
	Python py;
	py.content();
	py.footer();
	py.header();
	cout << "------------------" << endl;

	cout << "Cpp下载视频如下:" << endl;
	Cpp cp;
	cp.content();
	cp.footer();
	cp.header();
	cout << "------------------" << endl;

}

int main() {
	test1();


	return 0;
}

2.注意我上面的那句话,无法访问到,这就说明了,父类中私有成员也会被子类继承,只是由编译器隐藏后访问不到了。

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

class base {
public:
	int m_A;
private:
	int m_B;
protected:
	int m_C;
};

class son1 :public base {
public:
	int m_D;

};

//父类所有的非静态成员属性都会被子类继承下去
//父类中的私有成员属性是被编译器给隐藏了。因此访问不到,但是确实是被继承下来了

void test1() {
	cout << "son1 的大小为:" << sizeof(son1) << endl;
	//所以此处的大小是16;
}

int main(){
	test1();

	return 0;
}

3.继承中构造函数和析构函数的调用顺序

创建子类对象后,先构造父类,再构造子类,析构顺序相反。

4.同名成员处理

1)子类对象可以直接访问到子类中的同名成员,

2)子类对象加作用域可以访问到父类同名成员

3)当子类与父类拥有同名的成员函数,子类会隐藏父类中的同名函数,一定要加作用域才能够访问

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

class base {
public:
	int m_A=200;
	void func() {
		cout << "base-func()函数的调用" << endl;
	}
	void func(int a) {
		cout << "base-func(int a)函数的调用" << endl;
	}

};

class son1 :public base {
public:
	int m_A=100;
	void func() {
		cout << "son-func()函数的调用" << endl;
	}
};

//同名成员函数处理
void test1() {
	son1 s;
	//子类对象可以直接访问到子类中的同名成员
	s.func();
	//子类对象加作用域可以访问到父类的同名成员
	s.base::func();

	//当子类对象与父类对象拥有同名的成员函数,子类会隐藏父类中的同名函数,一定要加作用域才能够访问到哦
}

//同名成员属性处理
void test2() {
	son1 s;
	cout << s.m_A << endl;
	cout << s.base::m_A << endl;

}

int main() {
	test1();
	test2();

	return 0;
}

5.同名静态成员处理方式和非静态处理方式一样,只不过有两种访问方式(通过对象,通过类名)

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

class Base {
public:
	static int m_A;

	static void func() {
		cout << "Base-func()的调用" << endl;
	}
	static void func(int a) {
		cout << "Base-func(int a)的调用" << endl;
	}
};

int Base::m_A = 100;

class Son:public Base {
public:
	static int m_A;
	
	static void func() {
		cout << "Son-func()的调用" << endl;
	}

};

int Son::m_A = 200;

void test1() {
	//1.通过对象访问
	Son s;
	cout << "通过对象访问" << endl;
	cout << s.m_A << endl;
	cout << s.Base::m_A << endl;

	//2,通过类名访问
	cout << "通过类名访问" << endl;
	cout << Son::m_A << endl;
	//第一个 ::代表通过类名访问,第二个 ::代表作用域
	cout << Son::Base::m_A << endl;
}

int main() {
	test1();

	return 0;
}

//同名静态成员处理方式和非静态成员处理方式一样,只不过是有了两种访问方式
//通过类名和通过对象

6.多继承语法

1)c++允许一个类继承多个类(一个子类继承多个父类)

2)多继承可能会引发父类中有同名成员出现,需要加一个作用域区分

3)c++实际开发不建议用多继承(容易引发一些不必要的冲突)

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

class Base1 {
public:
	int m_A;
	Base1() {
		m_A = 100;
	}

};

class Base2 {
public:
	int m_A;
	Base2() {
		m_A = 200;
	}
};

class Son :public Base1, public Base2 {
public:
	int m_C;
	Son() {
		m_C = 300;
	}

};
void test1() {
	Son s;
	//大小依然是所有的数据大小之和
	cout << sizeof(s) << endl;
	//多继承中如果父类中出现了同名情况,子类使用的时候要加作用域
	cout << s.Base1::m_A << endl;
	cout << s.Base2::m_A << endl;

}

int main() {
	test1();


	return 0;
}

7.菱形继承

菱形继承带来的问题:

子类继承两份相同的数据,导致资源浪费和二义性。

可以用虚继承的方法来解决

所谓虚继承每一个派生类会给自己的子类继承虚基类指针(vbptr)和一份虚基类表,表中记录了虚基类与本类与本类的偏移量。

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

class Animal {
public:
	int m_Age;
};
//虚继承就是再继承方式的前面加上virtual关键字
//虚继承就是菱形继承问题的解决方法
class sheep:virtual public Animal{};

class tuo :virtual public Animal{};

class sheeptuo : public sheep, public tuo{};

void test1() {
	sheeptuo st;
	//两个派生类都从基类中继承了相同的数据,浪费空间,只需要留一份就好了
	//应用虚继承以后,两个派生类的子类只从其中继承了一份数据,且数据值以最后一次修改为准

	st.sheep::m_Age = 100;
	st.tuo::m_Age = 200;
	cout << st.sheep::m_Age << endl;
	cout << st.tuo::m_Age << endl;

}
int main() {
	test1();

	return 0;
}

多态:

一,基本介绍

多态分为两类:

1)静态多态:函数重载和运算符重载,复用函数名

2)动态多态:派生类和虚函数实现运行时多态。

区别:静态多态的函数地址早绑定---编译阶段确定函数地址

           动态多态的函数地址晚绑定---运行阶段确定函数地址

***c++中允许父子类之间的类型转换

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

class Animal {
public:
	//函数前面加上virtual关键字,变成虚函数,那么编译器在编译时就不能确定函数调用了
	//即:函数地址的先后绑定
	virtual void Speak() {
		cout << "动物在说话" << endl;
	}
};

class Cat :public Animal {
public:
	//重写:函数返回值类型  函数名 参数列表 完全一致
	//所以这里的virtual可写可不写
	virtual void Speak() {
		cout << "小猫在说话" << endl;
	}
};

class Dog :public Animal {
public:
	void Speak() {
		cout << "小狗在说话" << endl;
	}
};

//回调函数,提供多个选择
//c++中允许父子类之间的类型转化
void dospeak(Animal &animal) {
	animal.Speak();
}

void test1() {
	Cat cat;
	dospeak(cat);
	
	Dog dog;
	dospeak(dog);

}
int main() {
	test1();


	return 0;
}

二,详细的使用

1.动态多态满足条件:

1)有继承关系

2)子类重写父类的虚函数

2.使用动态多态:

父类的指针或引用指向子类对象

3.重写:函数返回值类型    函数名,参数列表完全一致称为重写。

4.多态的优点

1)代码组织结构清晰

2)可读性强

3)利于前期和后期的扩展以及维护

5.纯虚函数

virtual  返回值类型  函数名  (参数列表)=0;

6.抽象类特点:

1)无法实例化对象

2)子类必须重写抽象类中的纯虚函数,否则也属于抽象类。

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

class Base {
public:
	//纯虚函数
	//父类的虚函数是用不上的,写了也是白写,所以就出现了纯虚函数的概念
	virtual void func() = 0;
	//一旦有了纯虚函数,该类就称作抽象类
	//不能实例化对象
	//且子类也必须重写虚函数,不然子类也成了抽象类,也无法实例化对象
};

class Son : public Base {
public:
	void func() {
		cout << "func()函数的调用" << endl;
	}
};

void test1() {
	//多态的意义就是
	//定义一个父类的指针,指向它的任意一个我们想要使用的子类对象,
	//从而能使我们调用到该子类对象的函数实现
	Base* b = new Son;
	b->func();
	delete b;
}

int main() {
	test1();

	return 0;
}

7.虚析构和纯虚析构的共性

1)可以解决父类指针释放子类对象时不干净

2)都需要有具体的函数实现

区别:如果是纯虚析构,该类属于抽象类,无法实例化对象。

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;
#include<string>

class Animal {
public:
	virtual void Speak() = 0;
	Animal() {
		cout << "Animal构造函数的调用" << endl;
	}

	//利用纯析构可以解决父类指针释放子类对象时不干净的问题
	/*virtual ~Animal() {
		cout << "Animal析构函数的调用" << endl;
	}*/
	//如果不想让该类实例化对象,就写成纯虚析构
	virtual ~Animal() = 0;
};

Animal::~Animal() {
	cout << "Animal纯虚析构函数的调用" << endl;
}

class Cat :public Animal {
public:
	virtual void Speak() {
		cout << "小猫在说话" << endl;
	}
	Cat(string name) {
		cout << "Cat构造函数的调用" << endl;
		m_Name = new string(name);
	}

	~Cat() {
		cout << "Cat析构函数的调用" << endl;
		if (m_Name != NULL) {
			delete m_Name;
			m_Name = NULL;
		}
	}
	string* m_Name;
};

void test1() {
	Animal* a = new Cat("Tom");
	a->Speak();
	//父类指针在析构的时候,并不会调用子类中的析构函数,导致子类如果有堆区属性,会出现内存泄漏
	delete a;
}
int main() {
	test1();

	return 0;
}

8.实例

给大家两个实例练练手

1)

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;
//制作饮品-多态

class MakeDrink {
public:
	//煮水
	virtual void Boil() = 0;

	//冲泡
	virtual void Brew() = 0;

	//倒入杯中
	virtual void Pour() = 0;

	//加入辅料 
	virtual void Add() = 0;

	void makedrink() {
		Boil();
		Brew();
		Pour();
		Add();
	}
};

//制作咖啡
class Coffee : public MakeDrink {
public:
	//煮水
	virtual void Boil() {
		cout << "煮咖啡水" << endl;
	}

	//冲泡
	virtual void Brew() {
		cout << "冲泡咖啡" << endl;
	}

	//倒入杯中
	virtual void Pour() {
		cout << "倒入咖啡杯中" << endl;
	}

	//加入辅料 
	virtual void Add() {
		cout << "加入牛奶和糖" << endl;
	}
};

//制作茶
class Tea : public MakeDrink {
public:
	//煮水
	virtual void Boil() {
		cout << "煮茶水" << endl;
	}

	//冲泡
	virtual void Brew() {
		cout << "冲泡茶" << endl;
	}

	//倒入杯中
	virtual void Pour() {
		cout << "倒入茶杯中" << endl;
	}

	//加入辅料 
	virtual void Add() {
		cout << "加入茶叶" << endl;
	}
};

void dodrink(MakeDrink * md) {//MakeDrink * md = 子类对象
	md->makedrink();
	delete md;
}

void test1() {
	//MakeDrink* md = new Coffee;
	//MakeDrink& md = new Coffee;
	dodrink(new Coffee);
	cout << "-----------------" << endl;

	dodrink(new Tea);

}

int main() {
	test1();


	return 0;
}

2)制作电脑

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

//抽象不同的零件类
//抽象CPU类
class CPU {
public:
	virtual void calculate() = 0;
};

//抽象显卡类
class xianka{
public:
	virtual void display() = 0;
};

//抽象内存条类
class Memory {
public:
	virtual void storage() = 0;
};

//电脑类
class Computer {
public:
	Computer(CPU* cpu,xianka* xk,Memory* mem) {
		m_mem = mem;
		m_xk = xk;
		m_cpu = cpu;
	}
	//提供工作的函数
	void work() {
		m_cpu->calculate();
		m_xk->display();
		m_mem->storage();
	}

	~Computer() {
		if (m_cpu != NULL) {
			delete m_cpu;
			m_cpu = NULL;
		}
		if (m_xk != NULL) {
			delete m_xk;
			m_xk = NULL;
		}
		if (m_mem != NULL) {
			delete m_mem;
			m_mem = NULL;
		}
	}

private:
	CPU* m_cpu;
	xianka* m_xk;
	Memory* m_mem;
};

//具体厂商
//inter厂商
class IntelCPU :public CPU {
public:
	virtual void calculate() {
		cout << "Inter的CPU" << endl;
	}
};

class Intelxianka :public xianka {
public:
	virtual void display() {
		cout << "Inter的xianka" << endl;
	}
};

class IntelMemory :public Memory {
public:
	virtual void storage() {
		cout << "Inter的Memory" << endl;
	}
};

//联想厂商
class Lenovo :public CPU {
public:
	void calculate() {
		cout << "Lenovo的CPU" << endl;
	}
};

class Lenovoxianka :public xianka {
public:
	void display() {
		cout << "Lenovo的xianka" << endl;
	}
};

class LenovoMemory :public Memory {
public:
	void storage() {
		cout << "Lenovo的Memory" << endl;
	}
};

void test1() {
	//创建第一台电脑
	CPU* intelCpu = new IntelCPU;
	xianka* intelxianka = new Intelxianka;
	Memory* intelmem = new IntelMemory;

	Computer computer1(intelCpu, intelxianka, intelmem);
	computer1.work();
	
	cout << "------------------" << endl;
	//创建第二台电脑
	Computer computer2(new IntelCPU, new Intelxianka, new IntelMemory);
	computer2.work();
}
int main() {
	test1();

	return 0;
}

东西很多,希望大家认真学习,我后面会在B站上面更新讲解视频,希望大家捧场。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

喜欢吃豆

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

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

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

打赏作者

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

抵扣说明:

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

余额充值