C++11 —— 多态

C++11 —— 多态

  • C++多态
      • 示例一、通过基类指针调用派生类或基类成员函数
      • 示例二、通过派生类对象调用基类或派生类方法
      • 小结

C++多态

引用或指针类型的静态类型与动态类型不一致是C++语言支持多态性的根本原因。
(动态绑定只有在我们通过指针或引用调用虚函数时才会发生)

示例一、通过基类指针调用派生类或基类成员函数

#include 

class BaseA {
public:
    virtual void log() {
        std::cout << "this is log() in BaseA " << std::endl;
    }
    
    virtual void log(int i, int j) {
        std::cout << "this is log(int, int) in BaseA " << std::endl;
    }
   
    // 注意这不是一个虚函数   
    void test(int i) {
        std::cout << "this is test(int) in BaseA" << std::endl;
    }
};

class DerivedA : public BaseA{
public:
    virtual void log() override {
        std::cout << "this is log() in DerivedA " << std::endl;
    }
    
    // 注意这不是一个虚函数
    void log(int i) {
        std::cout << "this is log(int) in DerivedA " << std::endl;
    }
    
    // 注意这不是一个虚函数    
   void test(int i) {
        std::cout << "this is test(int) in DerivedA " << std::endl;
    }
};

int main() {
    DerivedA da;
    BaseA *bap = &da;
    bap->log(); // "this is log() in DerivedA " 
                // bap指针的静态类型为BaseA*, 动态类型为DerivedA*, log为虚函数,并且Derived重写了该虚函数,所以调用DerivedA的log

    bap->log(1,2); // "this is log(int, int) in BaseA ",log(int, int)是虚函数,发生动态绑定,所以编译器首先在DerivedA作用域中查找log(int,int),未找到
                   // 然后顺着继承链在BaseA中查找,找到,故调用BaseA中的log(int, int)
                   
    // bap->log(1); // log(int) 不是虚函数,未发生动态绑定,按照静态类型查找成员并调用,而BaseA没有一个log(int)的成员函数,所以编译报错
                
    bap->test(1); // "this is test(int) in BaseA"
                  // test(int)不是虚函数,所以未发生动态绑定,调用基类的test函数
    return 0;
}

示例二、通过派生类对象调用基类或派生类方法

class BaseA {
public:
    virtual void log() {
        std::cout << "this is log() in BaseA " << std::endl;
    }
    
    virtual void log(int i) {
        std::cout << "this is log(int) in BaseA " << std::endl;
    }
    
    void test(int i) {
        std::cout << "this is test(int) in BaseA" << std::endl;
    }
    
     void test(int i, int j) {
        std::cout << "this is test(int, int) in BaseA" << std::endl;
    }
    
    void success() {
        std::cout << "this is success() in BaseA" << std::endl;
    }
};

class DerivedA : public BaseA{
public:
    virtual void log() override {
        std::cout << "this is log() in DerivedA " << std::endl;
    }
    
    // 将在派生类中覆盖基类的所有log函数,即已派生类成员调用log函数时,基类中的所有log函数都被隐藏
    void log(int i, int j) {
        std::cout << "this is log(int) in DerivedA" << std::endl;
    }
    
    // 将在派生类中覆盖基类的所有test函数,即已派生类成员调用test函数时,基类中的所有test函数都被隐藏
   void test(int i, int j) {
        std::cout << "this is test(int, int) in DerivedA " << std::endl;
    }
};

int main() {
    DerivedA da;
    da.log(); // "this is log() in DerivedA ", 在DerviedA类作用域中查找log()函数,找到,调用
    da.log(1,2); // "this is log(int, int) in DerivedA", 在DerviedA类作用域中查找log()函数,找到,调用
    da.test(1,2); // "this is test(int, int) in DerivedA", 在DerviedA类作用域中查找log()函数,找到,调用
    
    // da.log(1); //由于派生类Derived中定义了log()函数,所以基类中的所有名为log的函数都将被隐藏,而在派生类中并不存在log(int),故报错
    // da.test(1); // 同上
    
    // 派生类的作用域嵌套在基类作用域中,所以当派生类对象在派生类作用域中未找到success成员时,
    // 将在基类作用域中继续查找success()成员,直到找到第一个符合要求的success成员或者到达继承链顶端为止
    da.success();  // "this is succcess() in BaseA"
    return 0;
}

小结

名字查找优先于类型查找。所以除了覆盖继承而来的虚函数之外,派生类最好不要重用定义在其他基类中的名字。否则会出现如上的调用错误。
我们在调用da.success()时,将遵循如下四个步骤:

  1. 首先确定da的静态类型,DerivedA
  2. 在da对应的静态类型对应的类中查找success()成员。如果找不到,则依次在基类中不断查找直至继承链的顶端。如果找遍了该类及其所有基类仍找不到,编译器将报错。
  3. 一旦找到了success(),进行常规检查,以确认本次调用是否合法。
  4. 假设合法,编译器根据调用的是否是虚函数产生不同的代码。结果为一个动态绑定的函数调用或普通函数调用。

你可能感兴趣的:(C++11,C++11,多态,虚函数,动态绑定,派生)