为什么要有虚函数

不用虚函数

#include <iostream>

class A {
public:
    void output() {
        std::cout << "I am parent." << std::endl;
    }
};

class B: public A {
public:
    void output() {
        std::cout << "I am child." << std::endl;
    }
};

int main() {
    std::shared_ptr<A> target1 = std::make_shared<B>();
    target1->output();

    std::shared_ptr<B> target2 = std::make_shared<B>();
    target2->output();

    return 0;
}
I am parent.
I am child.

用虚函数

#include <iostream>

class A {
public:
    // 只有这里不同,这里加了virtual
    virtual void output() {
        std::cout << "I am parent." << std::endl;
    }
};

class B: public A {
public:
    void output() {
        std::cout << "I am child." << std::endl;
    }
};

int main() {
    std::shared_ptr<A> target1 = std::make_shared<B>();
    target1->output();

    std::shared_ptr<B> target2 = std::make_shared<B>();
    target2->output();

    return 0;
}
I am child.
I am child.

分析

同样是指向B对象的指针,在运行期间,为什么没有加virtual的函数调用的却是父类函数,而没有加的调用的就是子类函数?

这里需要了解一个重要的概念——虚函数表,所有的虚函数都会存储在这个表里面。当我们给C++的指针赋予某一个类型的时候,其实并没有改变内存里面的任何东西,改变的只是解释内存的方式。现在A对象应为有了virtual,在实例化的时候就会初始化一个虚函数指针,和对应的额虚函数表,同时还有对应的内容——虚函数output()。

我们在实例化B对象的时候,虚函数表里面实际存放B对象的output实现。这样在调用的时候,就能从父类指针指向的内存里面,先找到虚函数指针,再找到虚函数表,从而完成对子类的调用,而这也就是多态的作用。

但如果没有virtual的时候,因为没有虚函数表,也就无法找到子类的实现。那么利用父类本身的内存空间描述,只能调用父类的函数了。

思考题

有纯虚函数的类为什么不能实例化?

纯虚函数是没有实现的,如果一个声明了纯虚函数的类被实例化,调用的时候应该如何处理是个问题,这样不如干脆禁止掉。

虚继承是怎么回事

虚拟继承在一般的应用中很少用到,所以也往往被忽视,这也主要是因为在C++中,多重继承是不推荐的,也并不常用,而一旦离开了多重继承,虚拟继承就完全失去了存在的必要因为这样只会降低效率和占用更多的空间。

参考:https://www.cnblogs.com/BeyondAnyTime/archive/2012/06/05/2537451.html

线性表使用虚函数主要是为了实现多态。在面向对象编程中,多态允许我们使用父类的指针或引用来操作不同的子类对象,调用的方法将是对应子类的方法。这样做的好处是可以编写更加灵活和可重用的代码。比如,在一个基类中定义虚函数,然后在各个派生类中重写这些虚函数,我们就可以通过基类的指针或引用来调用派生类的方法,而不需要关心具体的子类类型。 例如,在C++中,我们可能会有这样一个线性表的基类: ```cpp class LinearList { public: virtual void Insert(int value) = 0; virtual void Delete(int value) = 0; virtual ~LinearList() {} // 虚析构函数以确保派生类的析构函数被调用 }; ``` 各个派生类会根据自己的具体实现来重写这些方法: ```cpp class ArrayList : public LinearList { public: void Insert(int value) override { // ArrayList的插入逻辑 } void Delete(int value) override { // ArrayList的删除逻辑 } }; class LinkedList : public LinearList { public: void Insert(int value) override { // LinkedList的插入逻辑 } void Delete(int value) override { // LinkedList的删除逻辑 } }; ``` 通过使用虚函数,我们可以创建一个基类指针数组,然后用派生类对象来初始化这个数组: ```cpp LinearList* lists[] = {new ArrayList(), new LinkedList()}; lists[0]->Insert(1); // 调用的是ArrayList的Insert方法 lists[1]->Delete(1); // 调用的是LinkedList的Delete方法 ``` 这样,我们就可以通过基类指针来操作不同类型的线性表,而不需要关心具体是哪个派生类。多态的好处在于它增加了代码的可扩展性和可维护性,同时也减少了代码冗余。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值