Day15(上).多态强化

面向对象新需求及C++解决方案



#include <iostream>
using namespace std;

class HeroFighter
{
public:
	virtual int AttackPower()
	{
		return 10;
	}
};

class EnemyFighter
{
public:
	int DestoryPower()
	{
		return 15;
	}
};

class HeroAdv2Fighter : public HeroFighter
{
public:
	int AttackPower()
	{
		//this->AttackPower()
		return 20;
	}
protected:
private:
};

//这个类产生的时间
class HeroAdv3Fighter : public HeroFighter
{
public:
	int AttackPower()
	{
		//this->AttackPower()
		return 40;
	}
protected:
private:
};

//我写了一个框架 这个框架产生的时间
//一个模型 如果你把这个函数做成框架那。。。。。。。
void ObjFighter(HeroFighter *pBase, EnemyFighter *pEnemy)
{
	if (pBase->AttackPower() > pEnemy->DestoryPower())
	{
		printf("主角win\n");
	}
	else
	{
		printf("主角挂了\n");
	}
}

//面向对象三大概念
//封装  突破了c函数的概念
//继承  可以使用原来的代码 代码复用
//多态  比代码复用更高一个层次(可以调用未来)
//

//间接赋值成立的3个条件
/*
1 定义两个变量  1个实参1个形参
2、建立关联  实参取地址传给 形参
3、*p(实参的地址) 间接修改实参的值
*/

/*
多态成立的三个条件
	1 要有继承
	2 要有函数重写(虚)
	3、父类指针(父类引用)指向子类对象
*/

void main81()
{
	HeroFighter h1;
	EnemyFighter e1;
	HeroAdv2Fighter hAdvF;

	if (h1.AttackPower() > e1.DestoryPower())
	{
		printf("主角win\n");
	}
	else
	{
		printf("主角挂了\n");
	}

	if (hAdvF.AttackPower() > e1.DestoryPower())
	{
		printf("主角win\n");
	}
	else
	{
		printf("主角挂了\n");
	}

	system("pause");
}


void main()
{
	HeroFighter h1;
	EnemyFighter e1;
	HeroAdv2Fighter hAdvF;
	HeroAdv3Fighter hAdv3F;

	ObjFighter(&h1, &e1);
	ObjFighter(&hAdvF,  &e1); 

	ObjFighter(&hAdv3F,  &e1); 


	system("pause");
}



知识总结: --------重写  重载

函数重载

        必须在同一个类中

        子类无法重载父类的函数,父类同名函数将被名称覆盖

        重载是在编译期间根据参数类型和个数决定函数调用

函数重写:

        必须发生于子类和父类之间

        并且父类与子类中的函数必须有完全相同的原型

        使用virtural声明之后能够产生多态(如果不使用virtual,叫重定义)

        多态是在运行期间根据具体对象的类型决定函数调用

#include "iostream"
using namespace std;

class parent
{
public:
	void func()
	{
		cout << "parent func()" << endl;
	}
	void func(int a, int b)
	{
		cout << "parent func(a,b)" << endl;
	}
	virtual void func(int a, int b, int c)
	{
		cout << "parent func(a,b,c)" << endl;
	}
protected:
	
};

<span style="color:#FF0000;">//重写:父类和子类之间,函数三要素(函数名、函数参数、函数返回类型)完全一样
//重写分为两种:
//1. 如果父类有virtual关键字,这种父子之间的关系叫虚函数重写,这种情况发生多态(动态联编 迟绑定)
//2. 如果父类没有virtual关键字,这种父子之间的关系叫重定义(静态联编)</span>
class child1 :public parent
{
public:
	void func()
	{
		cout << "child1 func()" << endl;
	}
	//不加virtual是重写
	void func(int a, int b)
	{
		cout << "child1 func(a,b)" << endl;
	}
	//virtual这是多态
	virtual void func(int a, int b, int c)
	{
		cout << "child1 func(a,b,c)" << endl;
	}
private:
	
};

int rundunc(parent *pbase)
{
	cout << "pbase: ";
	pbase->func(1, 2, 3) ;
	return 0;
}
void main()
{
	parent p;
	//重载:在同一个函数里面,函数名相同,形参不同
	p.func();
	p.func(1, 2);
	p.func(1, 2, 3);

	child1 c;
	c.func();
	c.func(1, 2);
	c.func(1, 2, 3);

	cout << "*********************" << endl;

	//子类和父类都有func(),如何让子类调用父类的func()呢?
	child1 c1;
	c1.func();
	//在函数之前加上限制符
	c1.parent::func();

	//多态
	rundunc(&p);
	rundunc(&c1);

	system("pause");
}

多态的实现效果

      多态:同样的调用语句有多种不同的表现形态;

多态实现的三个条件

     有继承、有virtual重写、有父类指针(引用)指向子类对象。

多态的C++实现

      virtual关键字,告诉编译器这个函数要支持多态;不要根据指针类型判断如何调用;而是要根据指针所指向的实际对象类型来判断如何调用
多态的理论基础

     动态联编PK静态联编。根据实际的对象类型来判断重写函数的调用。

多态的重要意义

     设计模式的基础。

实现多态的理论基础

     函数指针做函数参数


C++中多态的实现原理

当类中声明虚函数时,编译器会在类中生成一个虚函数表

虚函数表是一个存储类成员函数指针的数据结构

虚函数表是由编译器自动生成与维护的

virtual成员函数会被编译器放入虚函数表中

存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr指针)





vptr指针的初始化过程是分步完成的:

1. 当指向父类的构造函数的时候

        C++编译器会初始化子类的vptr指针,让vptr指针指向父类的虚函数表

        如果你在父类的构造函数里面调用虚函数表print();

2. 当父类的构造函数执行完毕以后,再执行子类的构造函数,这个时候,让vptr指针真正的指向子类的虚函数表;


提个问题:构造函数中能调用虚函数,能实现多态吗?为什么?


下面看代码

#include "iostream"
using namespace std;

class parent
{
public:
	int i;
	int j;
public:
	virtual  void func()
	{
		cout << "parent func()" << endl;
	}
};

class child1 :public parent
{
public:
	//int k;  //#1  注意这里!!!
public:
	child1(int a, int b)
	{
		cout << "child1 构造函数" << endl;
	}
	void func()
	{
		cout << "child1 func()" << endl;
	}
};


void main()
{
	int i;
	parent *p = NULL;
	child1 *c = NULL;
	child1 cchar[3] = { child1(1, 2), child1(3, 4), child1(5, 6) };
	p = cchar;
	c = cchar;
	p->func();
	c->func();
	p++;
	c++;
	p->func();  //#1 取消注释,这里出错
	c->func();
	system("pause");
}
为什么#1不注释就不出错呢??????

首先分析以下内存模型图

这回明白了吧!!!如果子类中的成员变量完全是父类中的成员变量,不会出问题,如果子类成员变量中有父类没有的成员变量,那么子类的成员变量就会在原有的内存中加上自有的成员变量,这就导致父类和子类的内存空间发生不一致,当++的时候,父类和子类的步长就会发生异常,这将导致p++的头指针发生错误,如上图所示,所以尽量避免这种步长不一致的情景。


接下来是 另一个重要知识点,虚析构函数的多态。

#include "iostream"
using namespace std;

class parent
{
public:
	int i;
	int j;
public:
	parent(int a=0, int b=0)
	{
		cout << "parent 构造函数" << endl;
	}
	virtual  void func()
	{
		cout << "parent func()" << endl;
	}
	~parent()
	{
		cout << "parent 析构函数" << endl;
	}
};

class child :public parent
{
public:
	//int k;  //#1  注意这里!!!
public:
	child(int a, int b)
	{
		cout << "child 构造函数" << endl;
	}
	~child()
	{
		cout << "child 析构函数" << endl;
	}
	void func()
	{
		cout << "child func()" << endl;
	}
};


void main()
{
	child *c = new child(1, 2);
	c->func();
	delete c;

	system("pause");
}




这时候是没有任何问题的。但是如果改成下面的代码:

#include "iostream"
using namespace std;

class parent
{
public:
	int i;
	int j;
public:
	parent(int a=0, int b=0)
	{
		cout << "parent 构造函数" << endl;
	}
	virtual  void func()
	{
		cout << "parent func()" << endl;
	}
	~parent()
	{
		cout << "parent 析构函数" << endl;
	}
};

class child :public parent
{
public:
	//int k;  //#1  注意这里!!!
public:
	child(int a, int b)
	{
		cout << "child 构造函数" << endl;
	}
	~child()
	{
		cout << "child 析构函数" << endl;
	}
	void func()
	{
		cout << "child func()" << endl;
	}
};

<span style="color:#FF0000;">void deletefunc(parent *pBase)
{
	delete pBase;
}</span>
void main()
{
	child *c = new child(1, 2);
	c->func();
	//delete c;
	<span style="color:#FF0000;">deletefunc(c);</span>
	system("pause");
}



这时候发现子类的析构函数没有执行,这就需要我们采用多态的形式进行处理:

	virtual ~parent()
	{
		cout << "parent 析构函数" << endl;
	}

以上总结:

1. 重载是同一个类中,函数参数和个数不同,是静态联编;

2. 重写是,父类没有virtual,是重定义,是静态链表;

                 父类有virtual,会发生多态,是动态联编或迟绑定;

3.多态的原理!!!!











































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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值