C++语法学习笔记十五:基类指针-虚纯虚函数-多态性-虚析构

实例代码:


#include <iostream>
#include <string>
#include <vector>

using namespace std;

//基类

//类定义/ 类声明
class Human{
public:
	Human(){
		std::cout << " 执行了Human::Human() " << std::endl;
	}

	Human(int abc){
		std::cout << " 执行了Human::Human(int) " << std::endl;
	}

	virtual ~Human(){

	}
	void funcpub(){
		std::cout << " 执行了Human::funcpub() " << std::endl;
	}

	void somenamefunc(){
		std::cout << " 执行了Human::somenamefunc() " << std::endl;
	}

	void funchuman(){
		std::cout << " 执行了Human::funchuman() " << std::endl;
	}

	//声明成虚函数
	virtual void eat(){
		std::cout << " 人类吃各种粮食 " << std::endl;
	}

public:
	int m_age; //年龄
	char m_name[100]; //名字

}; // 类定义/ 类声明时 大家千万不要忘记末尾的;


//子类

//男人
class Men : public Human{  //表示Men 是Human的子类
public:
	Men(){
		std::cout << " 执行了Men::Men() " << std::endl;
	}

	Men(int abc){
		std::cout << " 执行了Men::Men(int) " << std::endl;
	}

	void somenamefunc(){

		Human::somenamefunc(); //调用父类somenamefunc函数
		std::cout << " 执行了Men::somenamefunc() " << std::endl;
	}

	void funcmen(){
		std::cout << " 执行了Men::funcmen() " << std::endl;
	}

	virtual void eat(){
		std::cout << " 男人喜欢吃米饭 " << std::endl;
	}

	//virtual void eat2() = 0; //纯虚函数,没有函数体,只有一个函数声明

public:
	using Human::somenamefunc;

};

//女人
class WoMen : public Human{  //表示Men 是Human的子类
public:
	WoMen(){
		std::cout << " 执行了WoMen::WoMen() " << std::endl;
	}

	WoMen(int abc){
		std::cout << " 执行了WoMen::WoMen(int) " << std::endl;
	}

	void somenamefunc(){
		Human::somenamefunc(); //调用父类somenamefunc函数
		std::cout << " 执行了WoMen::somenamefunc() " << std::endl;
	}

	void funcmen(){
		std::cout << " 执行了WoMen::funcmen() " << std::endl;
	}

	virtual void eat(){
		std::cout << " 女人喜欢吃面食 " << std::endl;
	}

public:
	using Human::somenamefunc;

};


int main() {
	//一:基类指针、派生类指针
	Men men1;
	Human* phuman = new Human();
	Men* pmen = new Men;

	//新玩法:父类指针可以new一个子类对象。父类指针很强大,很牛逼。
	Human* phuman5 = new Men; //这个是可以
	//Men *pmen = new Human; // 这个不可以
	phuman5->funchuman(); //父类类型指针,可以调用父类的成员函数
	//phuman->funcmen(); // 不可以,虽然你new子类对象,但是你是父类指针,无法调用子类成员函数。
	//既然父类指针没有办法调用子类的成员函数,那么你为什么还让父类指针new一个子类对象呢?有什么用处呢?


	//二:虚函数 
	//phuman->eat(); // 调用的是父类的eat函数,因为phuman是父类指针。
	//那如何调用Men和Women中的eat()函数。

	//Men *pmen = new Men;
	//pmen->eat(); // 调用子类Men中的eat();

	//WoMen *pwomen = new WoMen;
	//pwomen->eat(); // 调用子类WoMen中的eat();

	//问题:有没有一个解决办法,使我们只定义一个对象指针,就能够调用父类,以及各个子类的同名函数?
	//有: 这个对象指针,它的类型必须是父类类型。

	//我们如果想通过一个父类指针调用父类、子类中的同名同参的函数的话,对这个函数也是有要求:
		// 在父类中,eat函数声明之前必须要加virtual声明eat()函数成虚函数。
	//一旦某个函数(在基类)被声明成了虚函数,那么所有派生类(子类)中它都是虚函数。
	Human *phuman1 = new Men;
	phuman1->eat(); // 调用的是Men类的eat();
	phuman1->Human::eat(); // 是可以调用父类的eat;
	delete phuman1;

	Human *phuman2 = new WoMen;
	phuman2->eat(); // 调用的是WoMen类的eat();
	delete phuman2;

	Human *phuman3 = new Human;
	phuman3->eat(); // 调用的是Human类的eat();
	delete phuman3;

	// override
	//为了避免你在子类中写错虚函数,在C++11中,你可以在函数声明这里增加一个override关键字,
		//这个关键字 用在 “子类”中,而且是虚函数专用。
	//override就是用来说明派生类中的虚函数,你用了这个关键字之后,编译器就会认为你这个eat是覆盖了父类中的同名函数
	//(只有虚函数才存在子类可以覆盖父类中同名函数的问题),那么编译器就会在父类中找同名同参的虚函数,如果没找到,
	//编译器就会报错,这样,如果你不小心在子类中把虚函数写错了名字,写错了参数,编译器能够帮助你纠错。

	// final 也是虚函数专用,是用在“父类”,如果我们在父类的函数声明中加了final,那么任何尝试覆盖该函数的操作
	//都将引发错误。
	//调用虚函数执行的是“动态绑定”。动态:表示的就是在我们程序运行的时候才能知道调用了哪个子类的eat()虚函数。
	//动态的绑定到Men上去,绑到Women,取决于new的是Men还是Women;
	//动态绑定:运行的时候才决定你的phuman 对象绑定到哪个eat()函数上运行;
	Men men;
	men.eat(); //调用的肯定是Men;

	WoMen women;
	women.eat(); //调用的肯定是WoMen;

	Human human;
	human.eat(); //调用的肯定是Human;

	//三: 多态性
	//多态性 知识针对虚函数来说的;
	//随着虚函数的提出,面向对象编程里边有一个概念“多态性”,就浮出了水面。
	//多态性:体现在具有继承关系的父类和子类之间,子类重新定义(重写)父类的成员函数eat(),同时父类和子类中又把
	//这个eat()函数声明成了virtual虚函数。
	//通过父类的指针,只有到了程序运行时期,找到动态绑定到父类指针上的对象,这个对象它有可能是某个子类对象,也可能是父类对象,
	//然后系统内部实际上是要查一下虚函数表,找到函数eat()的入口地址,从而调用父类或者子类的eat()函数,这就是运行时期的多态性。

	//四: 纯虚函数
	//纯虚函数 是在基类中声明的虚函数,但是它在基类中没有定义,但是要求任何派生类都要定义该虚函数自己的实现方法。
	//基类中实现纯虚函数的方法是在函数原型后增加 = 0 ;
	//大家注意,一旦一个类中有纯虚函数了,那么你就不能生成这个类的对象了,这个类就成为了“抽象类”;
	//抽象类 不能用来生成对象,主要目的是用来统一管理子类对象。

	//所以,大家记住2点:
	//(1) 函数纯虚函数的类 叫抽象类,不能用来生成该类对象,主要用于当做基类来生成子类用的;
	//(2) 子类中必须要实现该基类中定义的纯虚函数;

	//五: 基类的析构函数一般写成虚函数(虚析构函数)
	//Men men2;
	//Men pmen = new Men;
	//delete pmen;

	//Human *phuman4 = new Men;
	//delete phuman4; // 没有执行子类的析构函数。坏事;

	//结论:用基类指针 new 子类的对象,在delete 的收系统不会调用派生类的析构函数。这肯定就有问题了;
	//如何解决:
	//我们如果把父类的析构函数写成虚函数,那么大家看:
	//大家记住: 在public继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员,
	//如果想要用基类对非继承成员进行操作,则要把基类的这个函数定义为虚函数,析构函数自然也应该如此。
	//另外就是基类中析构函数的虚属性也会被继承给子类,这样的话子类中的析构函数也就自然而然的成为了虚函数,
	//虽然名字和基类的析构函数名字不同,delete phuman的时候,肯定是要调用父类的析构函数,但在父类析构函数中它
	//要是想要调用子类Men的析构函数,那么 Human这个类中的析构函数就要声明为virtual的,也就是说C++中为了获得
	//运行时的多态行为,所调用的成员函数必须得是virtual的。

	//所以大家要记住结论:
	//如果一个类,想要做基类,我们务必要把这个类的析构函数写成virtual析构函数;
	//只要基类的析构函数是虚函数,就能够保证我们delet基类指针时能够运行正确的析构函数版本;

	//普通类 我们可以不写析构函数, 但如果是基类,就必须是一个析构函数,而且这个析构函数还必须是虚析构函数。


	system("pause");
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值