C++多态--虚函数virtual及override

参考链接:https://blog.csdn.net/i_chaoren/article/details/77281785

重点总结:

1 虚函数是让子类重新此方法用的。

2 虚函数写法有两种。(普通虚函数,纯虚函数)

普通虚函数

virtual f() { cout<<"hello"<<endl; }

纯虚函数(子类必须重写)

virtual f() = 0;

3 子类的该函数默认就是虚函数,建议声明出来,阅读清晰,并且子类重新的时候一定加上override,避免拼错单词或重载错函数,导致造了一个新函数而没能重载。

class Base {
public:
    virtual void f() = 0;
};

class A: public Base {
public:
    virtual void f() override { cout<<"hello world"<<endl; }
};

C++多态

C++多态(polymorphism)是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。

最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,动态绑定。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为“虚”函数。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。

用下面代码演示多态和非多态。

#include<iostream>
using namespace std;

class A
{
public:
    void foo()
    {
        printf("1\n");
    }
    virtual void fun()
    {
        printf("2\n");
    }
};

class B : public A
{
public:
    void foo()  //隐藏:派生类的函数屏蔽了与其同名的基类函数
    {
    printf("3\n");
    }
    void fun()  //多态、覆盖
    {
    printf("4\n");
    }
};

int main(void)
{
    A a;
    B b;
    A *p = &a;
    p->foo();  //输出1
    p->fun();  //输出2
    p = &b;
    p->foo();  //取决于指针类型,输出1
    p->fun();  //取决于对象类型,输出4,体现了多态
    return 0;
}

C++纯虚函数及虚函数

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0” 。

引入纯虚函数的原因:

1)为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。

2)在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。

虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。

虚函数是C++中用于实现多态的机制。核心理念就是通过基类访问派生类定义的函数。如果父类或者祖先类中函数func()为虚函数,则子类及后代类中,函数func()是否加virtual关键字,都将是虚函数。为了提高程序的可读性,建议后代中虚函数都加上virtual关键字。

C++保留字override

override 仅在成员函数声明之后使用时才是区分上下文的且具有特殊含义;否则,它不是保留的关键字。使用 override 有助于防止代码中出现意外的继承行为。以下示例演示在未使用override 的情况下,可能不打算使用派生类的成员函数行为。编译器不会发出此代码的任何错误。

class BaseClass
{
  virtual void funcA();
  virtual void funcB() const;
  virtual void funcC(int = 0);
  void funcD();
};

class DerivedClass: public BaseClass
{
  virtual void funcA(); // ok, works as intended

  virtual void funcB(); // DerivedClass::funcB() is non-const, so it does not
             // override BaseClass::funcB() const and it is a new member function

  virtual void funcC(double = 0.0); // DerivedClass::funcC(double) has a different
                   // parameter type than BaseClass::funcC(int), so
                   // DerivedClass::funcC(double) is a new member function
};

当使用 override时,编译器会生成错误,而不会在不提示的情况下创建新的成员函数。

class BaseClass
{
  virtual void funcA();
  virtual void funcB() const;
  virtual void funcC(int = 0);
  void funcD();
};

class DerivedClass: public BaseClass
{
  virtual void funcA() override; // ok

  virtual void funcB() override; // compiler error: DerivedClass::funcB() does not 
                  // override BaseClass::funcB() const

  virtual void funcC( double = 0.0 ) override; // compiler error: 
                         // DerivedClass::funcC(double) does not 
                         // override BaseClass::funcC(int)

  void funcD() override; // compiler error: DerivedClass::funcD() does not 
              // override the non-virtual BaseClass::funcD()
};

下面代码展示了手动调用虚函数的过程

#include<iostream>
using namespace std;

class A {
public:
    virtual void vfunc1() { cout << "A::vfunc1()" << endl; };
    virtual void vfunc2() { cout << "A::vfunc2()" << endl; };
    void func1() { cout << "A::func1()" << endl; };
    void func2() { cout << "A::func2()" << endl; };
private:
    int data1_;
    int data2_;
};

class B :public A {
public:
    virtual void vfunc1() override { cout << "B::vfunc1()" << endl; };
    void func2() { cout << "B::func2()" << endl; };
private:
    int data3_;
};

class C :public B {
public:
    virtual void vfunc1() override { cout << "C::vfunc1()" << endl; };
    void func2() { cout << "C::func2()" << endl; };
private:
    int data1_, data4_;
};

//演示了手动调用虚函数的过程
int main() {
    B a;
    typedef void(*Fun)(void);
    Fun pFun = nullptr;
    cout << "虚函数表地址:" << (int*)(&a) << endl;
    cout << "虚函数表第1个函数地址:"<<(int*)*(int*)(&a) << endl;
    cout << "虚函数表第2个函数地址:" << (int*)*(int*)(&a) + 1 << endl;
    pFun = (Fun)*((int*)*(int*)(&a));
    pFun();
    pFun = (Fun)*((int*)*(int*)(&a) + 1);
    pFun();
    return 0;
}
文章目录