C++ Virtual And Pure Virtual Explained

时间:2022-05-07
本文章向大家介绍C++ Virtual And Pure Virtual Explained,主要内容包括Virtual、Pure Virtual、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

Virtual

Virtual Function是成员函数,其行为在派生类中被覆盖。与非虚函数不同的是,即使没有关于类的实际类型的编译时信息,也会保留重写的行为。如果派生类使用指针或者对基类的引用进行处理,则对被覆盖的虚函数的调用将调用派生类中定义的行为。

#include <iostream>

struct Base {
    virtual void f() {
        std::cout << "basen";
    }
};
struct Derived : Base {
    void f() override { // 'override' is optional
        std::cout << "derivedn";
    }
};
int main()
{
    Base b;
    Derived d;
    
    // virtual function call through reference
    Base& br = b; // the type of br is Base&
    Base& dr = d; // the type of dr is Base& as  well
    br.f(); // prints "base"
    dr.f(); // prints "derived"
    
    // virtual function call through pointer
    Base* bp = &b; // the type of bp is Base*
    Base* dp = &d; // the type of dp is Base* as  well
    bp->f(); // prints "base"
    dp->f(); // prints "derived"
    
    // non-virtual function call
    br.Base::f(); // prints "base"
    dr.Base::f(); // prints "base"
}
//输出
base
derived
base
derived
base
base

虚函数使其类成为多态基类,派生类可以覆盖虚函数。 通过基类指针/引用调用的虚函数将在运行时解析。 也就是说,使用对象的动态类型而不是静态类型。静态类型是指不需要考虑表达式的执行期语义,仅分析程序文本而决定的表达式类型。静态类型仅依赖于包含表达式的程序文本的形式,而在程序运行时不会改变。动态类型是由一个左值表达式表示的左值所引用的最终派生对象的类型。 大致可以这么理解:

  • 静态类型:对象在声明时采用的类型,在编译期既已确定。
  • 动态类型:通常是指一个指针或引用目前所指对象的类型,是在运行期决定的。
  • 静态绑定:绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期。
  • 动态绑定:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期。

Pure Virtual

抽象类是定义或继承至少一个最终覆盖纯虚函数的类,一个纯虚函数隐含的使其自己的类被定义为抽象类,抽象类不能被实例化,只能通过派生类来覆盖实现所有继承的纯虚函数,如果派生类不覆盖实现所有的纯虚函数,那么程序编译不通过报错。何为纯虚函数?格式如下: declarator virt-specifier(optional) = 0 eg: virtual func(optional) = 0; =0只能出现在声明标识符之后或者可选标识符(virtual、final)之后,不能出现在成员函数定义中。

#include <iostream>

struct Base {
    virtual int g();
    virtual ~Base() {}
};

struct A : Base {
    // ok, declares three member virtual functions, two of them pure
    virtual int f() = 0;
	//override代表派生类覆盖父类g()方法
    virtual int g() override = 0;
    virtual int h();
    
    // ok, destructor can be pure too
    virtual ~A() = 0;
    
    // error: pure-specifier on a function definition
    virtual int b()=0 {}
};
//输出为
 virtual int b()=0 {} error: initializer on function does not look like a pure-specifier
struct Abstract {
    virtual void f() = 0; // pure virtual
    virtual void g() {}; // non-pure virtual
    ~Abstract() {
        g(); // okay, calls Abstract::g()
        // f(); // undefined behavior!
        Abstract::f(); // okay, non-virtual call
    }
};
 
//definition of the pure virtual function
void Abstract::f() { std::cout << "A::f()n"; }
 
struct Concrete : Abstract {
    void f() override {
        Abstract::f(); // OK: calls pure virtual function
    }
    void g() override {}
    ~Concrete() {
        g(); // okay, calls Concrete::g()
        f(); // okay, calls Concrete::f()
    }
};