文章目录
- 多态(Polymophism)
- 基类指针(引用)指向子类对象实现多态
- 不要在构造函数或者析构函数中调用虚函数
- 为什么不能滥用虚函数
- 使用virtual, override预防未知bug
- 使用`final`描述符限制函数或者类的继承
- 重载(overloading) vs 重写(overriding)
- 虚函数表(virtual table)
- 虚函数调用过程
- Reference:
多态(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 overridestd::string_view getName2(int x) const override { return "B"; } // compile error, function is not an overridestd::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 classesstd::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)
- 虚函数表是在做动态绑定(dynamic/late binding)时,处理函数调用时的查找表。
- 每个包含虚函数的类都有一张虚函数表,该表在编译时已经建立好。表中包含该类的对象可以访问的所有虚函数的entry。
- entry是一个函数指针,指向该类可以访问的最深层(most-derived)的函数
- 编译器给基类添加了一个隐藏的成员变量,虚指针:*__vptr. 虚指针在对象构建时被赋值,指向该类的虚函数表。
虚函数调用过程
- 使用*__vptr获取虚函数表
- 在虚函数表中找到对于的function
- 调用function
因此会比常规非虚函数调用慢一点。
Reference:
https://www.learncpp.com/