多态分为两类:
静态多态和动态多态的区别:
我知道你不理解(除非你是来复习的),一如既往,代码胜千言.
#include<iostream>
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<iostream>
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关键字(真的仅此而已!),这样就会将地址晚绑定.(自己复制试一下!)
止步于此就太可惜了(当然),我们将问题进一步延申,现在我们创建一个狗类并放入doSpeak函数里,我们看看会发生什么...
#include<iostream>
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();
}
运行结果:
现在你发现,现在的speak函数(不是doSpeak函数),地址是上来就确定的吗?当然不是.由于你传入的对象不同,传入猫就让猫说话,传入狗就走狗说话的代码.所以动态多态到底是啥呢?上面的定义可以给你一个参考,那么动态多态的满足条件又是啥呢?这里又有几点需要你注意:
还有一点比较隐蔽同时也比较重要,父类的指针或者引用,可以执行子类的对象(你发现了吗?)在代码doSpeak(再次提醒,不是speak函数)中,我们是用父类的引用(Animal &animal)来指向子类的对象(cat)(或者dog),指针指向子类对象在本页笔记将在下一章记述.
我是李承真,坚持学习,努力锻炼