c++中的虚函数

多态(Polymophism)

编译时多态(Compile-time polymorphism): 指的是通过编译器实现的多态。包含"函数重载"和"模板"的实现
运行时多态(Runtime polymorphism): 指的是运行时实现的多态。主要指通过虚函数实现的多态。

基类指针(引用)指向子类对象实现多态
#include <iostream>
#include <string_view>
class Base
{
protected:
    int m_value{};

public:
    Base(int value)
        : m_value{ value }
    {
    }

    virtual ~Base() = default;

    virtual std::string_view getName() const { return "Base"; }
    int getValue() const { return m_value; }
};

class Derived: public Base
{
public:
    Derived(int value)
        : Base{ value }
    {
    }

   std::string_view getName() const override { return "Derived"; }
};

int main()
{
    Derived derived{ 5 };
    std::cout << "derived is a " << derived.getName() << " and has value " << derived.getValue() << '\n';

    Base& ref{ derived };
    std::cout << "ref is a " << ref.getName() << " and has value " << ref.getValue() << '\n';

    Base* ptr{ &derived };
    std::cout << "ptr is a " << ptr->getName() << " and has value " << ptr->getValue() << '\n';

    return 0;
}
derived is a Derived and has value 5
ref is a Derived and has value 5
ptr is a Derived and has value 5
不要在构造函数或者析构函数中调用虚函数

因为在构造基类时,子类对象还没有被创建。在析构基类时,子类对象已经被析构。

为什么不能滥用虚函数

如果类中含有一个或多个虚函数,则构建对象时需要额外的空间来存放相关的指针,寻址虚函数比普通函数更耗性能。

使用virtual, override预防未知bug

在base class的虚函数前加virtual关键字
在子类的虚函数(包含析构函数)后加override描述符(不用加virtual),可以在编译期间就能发现程序潜在的问题

#include <string_view>
class A
{
public:
	virtual std::string_view getName1(int x) { return "A"; }
	virtual std::string_view getName2(int x) { return "A"; }
	virtual std::string_view getName3(int x) { return "A"; }
};

class B : public A
{
public:
	std::string_view getName1(short int x) override { return "B"; } // compile error, function is not an override
	std::string_view getName2(int x) const override { return "B"; } // compile error, function is not an override
	std::string_view getName3(int x) override { return "B"; } // okay, function is an override of A::getName3(int)

};
使用final描述符限制函数或者类的继承

final function

#include <string_view>
class A
{
public:
	virtual std::string_view getName() const { return "A"; }
};

class B : public A
{
public:
	// note use of final specifier on following line -- that makes this function not able to be overridden in derived classes
	std::string_view getName() const override final { return "B"; } // okay, overrides A::getName()
};

class C : public B
{
public:
	std::string_view getName() const override { return "C"; } // compile error: overrides B::getName(), which is final
};

final class

#include <string_view>
class A
{
public:
	virtual std::string_view getName() const { return "A"; }
};

class B final : public A // note use of final specifier here
{
public:
	std::string_view getName() const override { return "B"; }
};

class C : public B // compile error: cannot inherit from final class
{
public:
	std::string_view getName() const override { return "C"; }
};
重载(overloading) vs 重写(overriding)

重载: 同一作用域类的多个同名函数,通过传递不同的参数值,产生不同的行为
重写: 存在于子类和父类之间,基类指针指向子类对象,子类的行为覆盖了父类的行为

虚函数表(virtual table)
  1. 虚函数表是在做动态绑定(dynamic/late binding)时,处理函数调用时的查找表。
  2. 每个包含虚函数的类都有一张虚函数表,该表在编译时已经建立好。表中包含该类的对象可以访问的所有虚函数的entry。
  3. entry是一个函数指针,指向该类可以访问的最深层(most-derived)的函数
  4. 编译器给基类添加了一个隐藏的成员变量,虚指针:*__vptr. 虚指针在对象构建时被赋值,指向该类的虚函数表。
    在这里插入图片描述
虚函数调用过程
  1. 使用*__vptr获取虚函数表
  2. 在虚函数表中找到对于的function
  3. 调用function
    因此会比常规非虚函数调用慢一点。
Reference:

https://www.learncpp.com/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值