C++面试题2_多态

(Polymorphism)是面向对象编程的三大特性之一(封装、继承、多态),它让同一个接口在不同对象上表现出不同的行为。

一、什么是多态?

函数调用,在不同对象上产生不同的行为。

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

class Dog : public Animal {
public:
    void speak() override { cout << "汪汪汪" << endl; }
};

class Cat : public Animal {
public:
    void speak() override { cout << "喵喵喵" << endl; }
};

int main() {
    Animal* a1 = new Dog();
    Animal* a2 = new Cat();

    a1->speak(); // 输出:汪汪汪
    a2->speak(); // 输出:喵喵喵

    delete a1;
    delete a2;
}
表面上都是调用 Animal::speak(),但结果却不同-这就是多态。

C++中的多态类型
C++的多态分为两种:

三、运行时多态的三个条件:
C++实现运行时多态必须满足三个条件:
基类中定义虚函数(virtual )
派生类重写该虚函数
3.通过基类指针或引用调用该虚函数

四、底层实现机制(虚函数表 vtable)

编译器为含有虚函数的类自动生成:
每个虚函数表(vtable):存放各个虚函数的函数指针;每个对象中都有一个 虚表指针(vptr),指向类的虚表。

Animal对象
+----------------+
| vptr ----------|------> [ Animal vtable ]
+----------------+        +---------------------+
                          | speak() -> Animal::speak |
                          +---------------------+

Dog对象
+----------------+
| vptr ----------|------> [ Dog vtable ]
+----------------+        +---------------------+
                          | speak() -> Dog::speak   |
                          +---------------------+
当你执行 a1->speak()时,程序会
1.根据对象a1 找到它的Vptr;
2.通过 vptr 找到它的 vtable;
3.在虚表中查找 speak()的函数指针;
4.调用实际函数(Dog::speak()或cat::speak())

因此:
动态绑定是在运行时完成的;调用哪个函数取决于对象的实际类型。

五、关键细节
(1)为什么析构函数常写成虚函数?
因为:

Animal* p = new Dog();
delete p;
如果基类析构函数不是虚函数,则只会调用 Animal::~Animal(),不会调用 Dog::~Dog(),
会造成内存泄漏。

(2)纯虚函数与抽象类
如果基类只定义接口,不实现:

class Animal {
public:
    virtual void speak() = 0; // 纯虚函数
};
这种类称为 抽象类,不能实例化,只能作为接口或父类。

(3)虚表只生成一份
虚函数表是类级别的静态结构,而不是对象独有的。

六、总结对比
特性                                               静态多态                                                 动态多态

一句话总结
C++多态让同一个接口(函数调用)在不同对象上表现出不同的行为静态多态靠编译器决议(重载、模板),动态多态靠运行时虚表机制(virtual)。

二、为什么要引入抽象基类和纯虚函数?

一、先看背景:为什么不够用?
假设我们写一个程序,要管理不同的动物:

class Dog {
public:
    void speak() { cout << "汪汪汪" << endl; }
};

class Cat {
public:
    void speak() { cout << "喵喵喵" << endl; }
};
如果要写一个“让动物叫”的函数

void makeSound(Dog d) { d.speak(); }
void makeSound(Cat c) { c.speak(); }
问题
每新增一种动物,都要重载一次 makesound();
不同动物间缺少统一接口,无法通过一个“通用指针“来操作。

引出解决思路:抽象出共性
我们发现所有动物都会“叫”,于是可以提取一个公共接口:

class Animal {
public:
    virtual void speak() = 0;  // 纯虚函数(只定义行为,不给实现)
};
这里=0表示纯虚函数类中只要存在纯虚函数,这个类就是 抽象基类(abstract class),不能被实例化。

三、派生类负责具体实现

class Dog : public Animal {
public:
    void speak() override { cout << "汪汪汪" << endl; }
};

class Cat : public Animal {
public:
    void speak() override { cout << "喵喵喵" << endl; }
};
然后我们就可以这样写:

void makeSound(Animal* p) {
    p->speak();  // 多态调用
}

int main() {
    Dog d;
    Cat c;
    makeSound(&d);
    makeSound(&c);
}

汪汪汪
喵喵喵

此时:
Animal 只定义了"会叫“的抽象概念,具体“怎么叫”,交由子类决定;makesound()不再关心具体类型。

四、核心作用总结
目的                                                                                 说明

五、类比理解

可以把 抽象基类 + 纯虚函数 理解为:
"接口定义者”+“实现者
就像:
Animal :制定规则的人(所有动物都得能叫)cat :按规则提供自己实现的参与者DOg、

一句话总结
纯虚函数 是定义“接口“的方式
抽象基类 是这些接口的载体
它们让 C++的多态真正具备扩展性与统一性,是大型系统设计的基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值