hides-overloaded-virtual-function

309

重载父类的同名虚函数时会出现hides overloaded virtual function编译警告。
从字面上的意思其实就可以理解:重载的虚函数被隐藏了。

三个关键点:

  • 重载
  • 虚函数
  • 隐藏

这个编译警告之所以出现,是因为上面三个关键点,首先是发生了重载,子类重载了父类的函数,其次被重载的是虚函数,这时这个被重载的父类的虚函数将会被隐藏。
何为隐藏呢,应该是不能使用子类实例直接调用父类被隐藏的函数,调用时必须指定父类命名空间,往深了说也就是编译器如果在子类中发现了要使用的函数的名字,注意,是名字不包含函数签名,只要名字匹配上,就不会再去父类中去寻找这个名字的函数,即便子类中的函数参数不匹配,也不会再去父类中寻找。

其实去掉虚函数这个关键点,在c++中当重载了父类的函数时,隐藏同样会发生,举个例子:

class A {
public:
    void foo() {
        cout << "foo of A" << endl;
    }
};

class B : public A{
public:
    void foo(int i) {
        cout << "foo of B" << endl;
    }
};

int main(void)
{
    B *b = new B();
    b->foo(); //编译报错
    b->A::foo(); //应该指定命名空间A::
    b->foo(1);
    return 0;
}

当重载的父类函数为虚函数时,代码如下:

class A {
public:
    virtual void foo() { //将父类函数声明为虚函数
        cout << "foo of A" << endl;
    }
};

class B : public A{
public:
    void foo(int i) { //此处会有编译警告:"'B::foo' hides overloaded virtual function"
        cout << "foo of B" << endl;
    }
};

int main(void)
{
    B *b = new B();
    b->foo(); //编译报错依旧
    b->A::foo(); //应该指定命名空间A::
    b->foo(1);
    return 0;
}

编译错误依旧,但多了一个编译警告,关于这个编译警告,有种解释说是为了避免书写错误,这就要说到多态,上面的例子没有应用多态有些不合适,修改一下:

class BookA; //改动处
class BookB; //改动处

class A {
public:
    virtual void foo(BookA *) { //改动处
        cout << "foo of A" << endl;
    }
};

class B : public A{
public:
    void foo(BookB *) { //改动处
        cout << "foo of B" << endl;
    }
};

int main(void)
{
    A *b = new B(); //改动处
    b->foo(new BookB());
    return 0;
}

与之前的例子的区别是多声明了两个类:BookABookB,函数的参数类型换成了这两个类,最重要的一点是main函数中将实例B声明为了A类型,此时就可以比较清晰的看出为什么这个警告是为了书写错误:

假设我们就是要在B类中重写父类A的void foo(BookA *)函数,以便在main函数中实现以类型A的实例去调用B对象的重写函数(多态调用),但却因为书写错误,把B类行的函数的参数写成了BookB *,这就与初衷不符了,此时这个编译警告就是有价值的了。

再假设,我们就是要在B类中声明void foo(BookB *)这么一个函数,且并不是为了重写父类中的函数,那么此时这个编译警告就是多余的,我们可以使用using来避免这个警告,例如在类B中做出如下声明:

class B : public A{
public:
    using A::foo;
    void foo(BookB *) {
        cout << "foo of B" << endl;
    }
};

这样就是告诉编译器,我们明确要使用这两个函数,并不是为了重写,这样有一个可能不期望出现的情况,就是B的实例也可调用void foo(BookA *)函数,如果不想这个情况发生,可以把using放到私有的里面,这样既解决了编译警告,又不会暴露父类的这个函数出去:

class B : public A{
public:
    void foo(BookB *) {
        cout << "foo of B" << endl;
    }
private:
    using A::foo;
};

参考了一个帖子:
https://stackoverflow.com/questions/18515183/c-overloaded-virtual-function-warning-by-clang