类和对象-多态(1)

这段抽象复杂,笔记会多一点

多态是C++面向对象的三大特性之一.

多态分为两类:

  • 静态多态:函数重载,运算符重载(operatorXXX,还记得吗?),复用函数名;
  • 动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态的区别:

  • 静态函数的函数地址早绑定 - 编译阶段确定函数地址
  • 动态函数的函数地址晚绑定 - 运行阶段确定函数地址
     

我知道你不理解(除非你是来复习的),一如既往,代码胜千言.

#include
using namespace std;

class Animal
{
public:
	void speak()
	{
		cout << "动物在说话" << endl;
	}
};

//猫类
class Cat : public Animal
{
	void speak()
	{
		cout << "小猫在说话" << endl;
	}
};
//执行说话的函数 
void doSpeak(Animal& animal)
{
	animal.speak();
}
void test01()
{
	Cat cat;
	doSpeak(cat);//父类的引用,指针可以直接指向子类对象
}
int main() {
	test01();
}

思考:运行结果是猫说话还是动物说话?

虽然我们设计cat类的初衷是想让cat去说话, 但是, 实际上这段代码的输出结果是动物在说话.

重点:

这是因为,执行说话的函数, 地址早绑定, 在编译的阶段就确定了函数的地址. to be more percisely, that is, 无论我们在doSpeak传进去猫类或者是狗类(自己创建一个!),都会走Animal里面的speak函数,结果就是,你看到的,都会输出"动物在说话".

解决方式?

如果想让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定,即地址晚绑定.关键字还是virtual, 这和上一篇的菱形继承的关键字是一样的(你还记得吗?). 看代码:

#include
using namespace std;

class Animal
{
public:
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
};

//猫类
class Cat : public Animal
{
	void speak()
	{
		cout << "小猫在说话" << endl;
	}
};
//执行说话的函数 
void doSpeak(Animal& animal)
{
	animal.speak();
}
void test01()
{
	Cat cat;
	doSpeak(cat);//父类的引用,指针可以直接指向子类对象
	//可是现在到底是 猫在说话 还是 动物在说话
}
int main() {
	test01();
}

这段代码和上一段(几乎)没有任何差别! 好吧, 你仅仅会发现在Animal类里面的speak函数的最前面加了一个virtual关键字(真的仅此而已!),这样就会将地址晚绑定.(自己复制试一下!)

类和对象-多态(1)_第1张图片

止步于此就太可惜了(当然),我们将问题进一步延申,现在我们创建一个狗类并放入doSpeak函数里,我们看看会发生什么...

#include
using namespace std;

class Animal
{
public:
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
};

//猫类
class Cat : public Animal
{
	void speak()
	{
		cout << "小猫在说话" << endl;
	}
};
//狗类
class Dog : public Animal
{
	void speak()
	{
		cout << "小狗在说话" << endl;
	}
};
//执行说话的函数 
void doSpeak(Animal& animal)
{
	animal.speak();
}
void test01()
{
	Cat cat;
	doSpeak(cat);
	Dog dog;
	doSpeak(dog);
}
int main() {
	test01();
}

运行结果:

类和对象-多态(1)_第2张图片

现在你发现,现在的speak函数(不是doSpeak函数),地址是上来就确定的吗?当然不是.由于你传入的对象不同,传入猫就让猫说话,传入狗就走狗说话的代码.所以动态多态到底是啥呢?上面的定义可以给你一个参考,那么动态多态的满足条件又是啥呢?这里又有几点需要你注意:

多态满足条件

  1. 有继承关系(dog,cat都是Animal的子类)
  2. 子类要重写父类的虚函数(无论是dog还是cat类,都把speak函数重新写了一遍.)(重写:返回值类型,函数名称,参数列表都需要完全相同,但是子类重写的时候virtual可写可不写,但是父类的虚函数,virtual必须写!所以,重写并不等于重载.)

多态使用条件

  • 父类指针或引用指向子类对象.

一个隐蔽的东西

还有一点比较隐蔽同时也比较重要,父类的指针或者引用,可以执行子类的对象(你发现了吗?)在代码doSpeak(再次提醒,不是speak函数)中,我们是用父类的引用(Animal &animal)来指向子类的对象(cat)(或者dog),指针指向子类对象在本页笔记将在下一章记述.

我是李承真,坚持学习,努力锻炼

你可能感兴趣的:(c++,开发语言)