C++中的虚函数重载

在一次修改代码过程中踩的坑,下来研究了一下,发现C++中虚函数重载后会产生很多有意思的情况,在这里总结了一下。

C++中有重载(overload)和重写(override)以及重定义这几个概念,1 overload:指的是相同作用域中的两个函数的函数名相同,但参数列表的个数、顺序、类型不同。而override指的是子类中重新定义的父类的虚函数。
2 override:overload要求两个函数的参数列表必须不同,但是不要求这两个函数必须是虚函数。而override要求必须是虚函数且父类的虚函数必须有virtual关键字,函数的参数列表和返回值也必须相同。子类中override的虚函数的访问修饰符可以不同。
3 重定义也是描述分别位于父类与子类中的同名函数的,但返回值可以不同。如果参数列表不同,这时子类中重定义的函数不论是否有virtual关键字,都会隐藏父类的同名函数。
如果参数列表相同,但父类中的同名函数没有virtual关键字修饰,此时父类中的函数仍然被隐藏。

虚函数的典型用法是:

#include 
using namespace std; 

class Base{
public:
    virtual void f();

};

void Base::f(){
    cout << "Base::f()" << endl;
}

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

void Derived::f(){
    cout << "Derived::f()" << endl;
}

int main() {
    Base *p1 = new Base;
    Base *p2 = new Derived;
    p1->f();
    p2->f();
    
    delete p1;
    delete p2;

    return 0;
}

程序的输出是:

Base::f()
Derived::f()

那么如果虚函数之间又发生了overload,会出现什么情况?
我们先看最简单的情况:

#include 
using namespace std; 

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

void Base::f(int a){
    cout << "Base::f(int) " << a << endl;
}

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

void Derived::f(){
    cout << "Derived::f()" << endl;
}

int main() {
    Base *p1 = new Base;
    p1->f(1);
    
    Base *p2 = new Derived;
    p2->f();
    
    delete p1;
    delete p2;
    
    return 0;
}

编译上面的代码,会发生如下错误:

test.cpp: In function ‘int main()’:
test.cpp:28:11: error: no matching function for call to ‘Base::f()’
     p2->f();
           ^
test.cpp:10:6: note: candidate: virtual void Base::f(int)
 void Base::f(int a){
      ^
test.cpp:10:6: note:   candidate expects 1 argument, 0 provided

这就是因为父类中虚函数的参数列表已经发生变化,这时不论子类中重定义的函数不论是否有virtual关键字,都会隐藏父类的同名函数。这时子类中只是重定义了一个自己的函数virtual void f(),而并没有override父类中对应的虚函数。
p2是一个指向Base类型的指针,根据虚函数的特性,对p2→f();的处理取决于是否override了父类的虚函数,如果没有,仍然会调用调用父类中被override的虚函数,但是现在父类中的函数已经成为了virtual void f(int),因此在执行p2→f()时会由于缺少输入参数而出现上述错误。
为了证明上述论断,可以在执行p2→f()时传入参数来判断:

#include 
using namespace std; 

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

void Base::f(int a){
    cout << "Base::f(int) " << a << endl;
}

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

void Derived::f(){
    cout << "Derived::f()" << endl;
}

int main() {
    Base *p1 = new Base;
    p1->f(1);
    
    Base *p2 = new Derived;
    p2->f(2);
    
    delete p1;
    delete p2;
    
    return 0;
}

这时可以通过编译,执行结果为

Base::f(int) 1
Base::f(int) 2

可以看到,子类自己定义的virtual void f()其实是父类的virtual void f(int)的一个重定义的函数,这时尽管p2实际指向了一个Derived对象,但由于没有override父类对应的虚函数,在执行 p2→f(2)时将执行父类的virtual void f(int)。
也可以这样修改:

#include 
using namespace std; 

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

void Base::f(int a){
    cout << "Base::f(int) " << a << endl;
}

void Base::f(){
    cout << "Base::f() " << endl;
}

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

void Derived::f(){
    cout << "Derived::f()" << endl;
}

int main() {
    Base *p1 = new Base;
    p1->f(1);
    
    Base *p2 = new Derived;
    p2->f();
    
    delete p1;
    delete p2;
    
    return 0;
}

因为父类中定义了可被子类override的函数,所以这时执行p2→f()又会重新执行子类的virtual void f():

Base::f(int) 1
Derived::f()

我们甚至还可以这样验证:

#include 
using namespace std; 

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

void Base::f(int a) {
    cout << "Base::f(int) " << a << endl;
}

void Base::f() {
    cout << "Base::f() " << endl;
}

class Derived:public Base {
public:
    virtual void f(float);
};

void Derived::f(float a) {
    cout << "Derived::f(float)" << showpoint << a << endl;
}


int main() {
    Base *p1 = new Base;
    p1->f(1);
    
    Base *p2 = new Derived;
    p2->f(**2.0**);
    
    delete p1;
    delete p2;
    
    return 0;
}

这时输出仍然为

Base::f(int) 1
Base::f(int) 2

这说明如果通过指向父类的指针,调用虚函数时,如果子类重定义了该虚函数(参数列表发生变化),则实际调用的仍是父类中的虚函数。
上面都是通过指向父类的指针来调用虚函数的,那么如果通过指向子类的指针调用虚函数会发生什么:

#include 
using namespace std; 

class Base {
public:
    virtual void f(int);
    virtual void f();

};

void Base::f(int a) {
    cout << "Base::f(int) " << a << endl;
}

void Base::f() {
    cout << "Base::f() " << endl;
}

class Derived:public Base {
public:
    virtual void f(float);
};

void Derived::f(float a) {
    cout << "Derived::f(float)" << showpoint << a << endl;
}

int main() {
    Base *p1 = new Base;
    p1->f(1);
    
    Derived *p2 = new Derived;
    p2->f(2.0);
    p2->f(3);
    
    delete p1;
    delete p2;
    
    return 0;
}

这时输出就变为了:

Base::f(int) 1
Derived::f(float)2.00000
Derived::f(float)3.00000

这说明,如果通过指向子类的指针调用虚函数,并且子类重定义了父类的虚函数,这时实际调用的就将是子类中的虚函数。


你可能感兴趣的:(c_cpp)