C++语法|虚函数与多态详细讲解(六)|如何解释多态?(面试向)

系列汇总讲解,请移步:
C++语法|虚函数与多态详细讲解系列(包含多重继承内容)

多态分为了两种,一种是静态的多态,一种是动态的多态。

静态(编译时期)的多态

函数重载

bool compare(int, int) {}
bool compare(double, double) {}

函数名字都是一个名字,但是参数个数都不相同,最终我们代码里面调用的是哪个函数,编译器是知道的:
函数源码和汇编如下

compare(10, 20); //call compare_int_int
compare(10.5, 20.5); //call compare_double_double

从汇编代码可以看出,到底调用哪个函数,编译时就已经确定了。

模版(函数模版和类模版)

函数模版?模版函数?深入理解函数与模板与分文件编写,我在这篇文章中的第一部分CPU眼里的模版明确写出了底层的汇编代码。具体可以去看文章。

template<typename T>
bool compare(T a, T b){}

compare<int>(10, 20); //call compare<int>(int, int)
compare<double>(10.2, 20.2); //call compare<double>(double, double)

动态(运行时期)的多态

这里当然是我们回答的重点。

动态的多态指的是在继承结构中,基类指针(引用)指向派生类对象,通过该指针(引用)调用同名覆盖方法(虚函数),基类指针指向哪个派生类对象,就会调用哪个派生类对象的覆盖方法,称为多态!

怎么做到的呢?就是通过动态绑定来实现的。

指针指向的是什么类型的派生类对象,那就访问谁的虚函数指针vfptr,进而就访问谁的虚函数表vftable,最后从虚函数表拿出来的,当然就是对应的派生类对象的方法了。

面试回答以上内容基本就差不多了,下面我们一起来做一点儿实验:

多态实践

问题引入

我们提供以下类:
关系图如下:

class Animal {
public:
    Animal(string name) : _name(name) {}
    virtual void bark() {}
protected:
    string _name;
};

//一下是动物实体类
class Cat : public Animal {
public:
    Cat(string name) : Animal(name) {}
    virtual void bark() { cout << _name << " bark: miao miao !" << endl; }
};

class Dog : public Animal {
public:
    Dog(string name) : Animal(name) {}
    void bark() { cout << _name << " bark: wang wang !" << endl; }
};

class Pig : public Animal {
public:
    Pig(string name) : Animal(name) {}
    void bark() { cout << _name << " bark: heng heng !" << endl; }
};

提供一个全局方法,同时测试函数如下:

void bark(Cat &cat) { cat.bark(); }
void bark(Dog &dog) { dog.bark(); }
void bark(Pig &pig) { pig.bark(); }
int main () {
	Cat cat("猫咪");
	Dog dog("二哈");
	Pig pig("佩奇");
	bark(cat);
	bark(dog);
	bark(pig);
}

打印的话大家肯定也能想象出来,但是里面有一个问题:

  1. 如果我们想继承Animal继承更多的新动物呢?
  2. 由于需求变更,我们可能删除现有的动物?

那么这组API的设计是不是不太合理呢?

//这里的bark API接口无法做到我们软件设计的“开-闭”原则
//即对修改关闭,对扩展开放
void bark(Cat &cat) { cat.bark(); }
void bark(Dog &dog) { dog.bark(); }
void bark(Pig &pig) { pig.bark(); }

如果我们添了一个动物,我们就需要添加一个bark函数;如果删除一个动物,就需要删除一个bark函数,太不合理了!

这样的设计不能满足高内聚,低耦合的要求。

问题解决

还记得本系列中不断提及的,指针或引用的动态绑定是多态性的基础吗?我们现在这样写代码:

void bark (Animal *p) { p->bark(); } //Animal::bark是一个虚函数,所以是动态绑定
void bark (Animal &p) { p.bark(); }
int main () {
	Cat cat("猫咪");
	Dog dog("二哈");
	Pig pig("佩奇");
	bark(&cat);
	bark(&dog);
	bark(&pig);
    bark(cat);
	bark(dog);
	bark(pig);
}

使用指针或者引用,都能够完美解决问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值