虚函数的性质

一、基础概念题

  1. 什么是虚函数?作用是什么?
    定义:用virtual关键字声明的成员函数,允许子类重写(override)以实现多态。
    作用:
    实现运行时多态(动态绑定),通过基类指针/引用调用子类重写的函数。
    支持接口抽象(如纯虚函数定义抽象类)。
    示例:
    cpp
    class Base {
    public:
    virtual void show() { cout << “Base” << endl; }
    };
    class Derived : public Base {
    public:
    void show() override { cout << “Derived” << endl; } // C++11推荐用override
    };
    Base* obj = new Derived();
    obj->show(); // 输出"Derived"(动态绑定)
  2. 虚函数与普通函数的区别?
    特性 虚函数 普通函数
    绑定时机 运行时(动态绑定) 编译时(静态绑定)
    性能开销 稍高(需查虚函数表) 无额外开销
    是否可重写 必须用override(C++11后) 不可重写(除非隐藏)
    构造函数/析构函数 析构函数通常应为虚函数 无特殊要求
    二、实现原理题
  3. 虚函数是如何实现的?
    虚函数表(vtable):
    每个包含虚函数的类有一个虚函数表,存储虚函数地址。
    对象内存中包含一个指向虚函数表的指针(vptr)。
    调用虚函数时,通过vptr找到虚函数表,再根据索引调用对应函数。
    示例:
    cpp
    class Base {
    public:
    virtual void f() {}
    virtual void g() {}
    };
    // Base的虚函数表布局:
    // [ &Base::f, &Base::g ]
  4. 为什么构造函数不能是虚函数?
    原因:
    对象构造时,vptr尚未初始化(子类构造前需先构造基类部分)。
    虚函数依赖vptr,若构造函数为虚函数,无法确定调用哪个类的实现。
    替代方案:使用工厂模式或静态成员函数创建对象。
  5. 为什么析构函数通常要声明为虚函数?
    问题:若基类指针指向子类对象,删除时若析构函数非虚,仅调用基类析构函数,导致子类资源泄漏。
    示例:
    cpp
    class Base {
    public:
    ~Base() { cout << “Base destructor” << endl; } // 非虚析构函数
    };
    class Derived : public Base {
    public:
    ~Derived() { cout << “Derived destructor” << endl; }
    };
    Base* obj = new Derived();
    delete obj; // 仅调用Base::~Base(),Derived部分未释放
    解决:将基类析构函数声明为virtual,确保调用链正确。
    三、应用场景题
  6. 何时需要使用虚函数?
    场景:
    需要通过基类接口操作不同子类对象(如插件系统、图形渲染)。
    实现策略模式或模板方法模式。
    示例:
    cpp
    class Shape {
    public:
    virtual double area() = 0; // 纯虚函数(抽象类)
    };
    class Circle : public Shape {
    public:
    double area() override { return 3.14 * radius * radius; }
    };
  7. 虚函数能否是内联函数?
    答案:可以声明为内联,但编译器通常忽略内联请求(动态绑定需运行时确定函数地址)。
    例外:若通过对象(而非指针/引用)调用虚函数,且编译器能确定具体类型,可能内联。
  8. 虚函数能否是静态函数?
    答案:不能。静态函数属于类而非对象,无this指针,无法支持多态。
    四、陷阱与高级题
  9. 虚函数调用被“截断”的情况?
    问题:若子类未重写虚函数,仍调用基类实现。
    示例:
    cpp
    class Base {
    public:
    virtual void foo() { cout << “Base::foo” << endl; }
    };
    class Derived : public Base {
    // 未重写foo()
    };
    Derived d;
    d.foo(); // 调用Base::foo(无截断)
    Base* b = &d;
    b->foo(); // 仍调用Base::foo(若Derived未重写)
  10. 虚函数与final关键字(C++11)
    作用:禁止子类重写虚函数。
    示例:
    cpp
    class Base {
    public:
    virtual void foo() final {} // 禁止子类重写
    };
    class Derived : public Base {
    public:
    void foo() override {} // 编译错误:尝试重写final函数
    };
  11. 虚函数与多继承的虚函数表
    问题:多继承时,对象可能包含多个vptr(每个基类一个),虚函数调用需正确选择虚函数表。
    示例:
    cpp
    class Base1 {
    public:
    virtual void f() {}
    };
    class Base2 {
    public:
    virtual void f() {}
    };
    class Derived : public Base1, public Base2 {
    public:
    void f() override { /* 重写哪个Base的f? */ }
    };
    解决:使用作用域运算符明确指定重写哪个基类的虚函数(如Base1::f())。
    五、综合代码题
  12. 以下代码的输出是什么?为什么?
    cpp
    class Base {
    public:
    virtual void func() { cout << “Base” << endl; }
    };
    class Derived : public Base {
    public:
    void func() override { cout << “Derived” << endl; }
    };
    int main() {
    Base* b = new Derived();
    b->func(); // 输出?
    delete b;
    return 0;
    }
    答案:输出Derived。
    原因:func()是虚函数,通过基类指针调用时动态绑定到子类实现。
  13. 如何避免虚函数调用的性能开销?
    方法:
    使用CRTP(奇异递归模板模式)实现静态多态。
    在性能关键路径中,若类型可确定,直接调用具体类函数(绕过虚函数表)。
    示例(CRTP):
    cpp
    template
    class Base {
    public:
    void interface() {
    static_cast<T*>(this)->implementation(); // 静态绑定
    }
    };
    class Derived : public Base {
    public:
    void implementation() { cout << “Derived” << endl; }
    };
    总结
    核心考点:虚函数表机制、多态实现、析构函数虚化、override/final关键字。
    准备建议:
    手动绘制虚函数表示意图。
    编写代码验证虚函数调用行为。
    理解虚函数与性能、内存布局的关系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值