1 引言
C++作为一门强大的编程语言,广泛用于开发各种类型的应用程序,特别是在面向对象编程领域。在C++中,虚函数和重写函数是面向对象编程的关键概念,它们允许我们建立强大的继承层次结构和多态行为。默认参数则为函数提供了额外的灵活性,使我们可以为函数的参数提供默认值。然而,当这两个概念结合在一起时,可能会引发意想不到的问题和错误。
如果你曾经在C++编程中遇到过 Default arguments on virtual or override methods are prohibited 的警告,那么本文将为你提供宝贵的见解和解决方案。
2 基本概念
在C++中,虚拟函数和重写函数是面向对象编程的关键概念之一。它们允许你创建多态性,这意味着你可以在基类中定义一个函数,然后在派生类中重写(覆盖)这个函数,以实现不同的行为。
2.1 虚函数
虚函数是在基类中声明为 **virtual **的成员函数。这意味着虚拟函数的行为取决于实际对象的类型,而不是引用或指针的类型。在基类中声明虚拟函数时,使用 virtual 关键字。
class Base {
public:
virtual void foo() {
// 基类的虚拟函数实现
}
};
2.2 重写函数
当你在派生类中重新定义(重写)基类中的虚拟函数时,它被称为重写函数。重写函数的声明必须与基类中的虚拟函数完全匹配,包括参数列表和返回类型。一般建议在定义重写函数时,使用 **override **关键字。
class Derived : public Base {
public:
void foo() override {
// 派生类的虚拟函数实现,覆盖基类的foo()
}
};
3 错误场景重现
以下测试修改自 C++虚函数中的默认参数_虚函数尽量不要提供默认参数-CSDN博客,可以帮助你直观的看出虚函数带默认参数可能带来的问题。
class Base {
public:
Base();
~Base();
virtual void setNumber(int num = 2) = 0;
};
class A :public Base {
public:
A();
~A();
virtual void setNumber(int num = 4) override{
qDebug() << num;
};
};
// Demo 1
Base* a1 = new A;
a1->setNumber(9);
// Demo 2
Base* a2 = new A;
a2->setNumber();
// Demo 3
A* a3 = new A;
a3->setNumber();
显然,以上代码我们期望的输出是:
9
4
4
但是实际上我们会输出:
9
2
4
4 分析原因
由Demo 2可以看出,虚函数调用了派生类重写的方法,但是没有调用派生类的默认参数。这个结果看起来有些矛盾:虚函数允许在运行时动态绑定到子类,因此父类才得以实现调用派生类重写后的方法。那么为什么没有调用派生类的默认参数呢?
在C++中,虚拟函数和重写函数的特性意味着编译器在运行时动态地选择要调用的函数版本,这是通过虚函数表(vtable)和指针来实现的。当你在派生类中重写基类的虚拟函数时,编译器将替换虚函数表中的相应条目,以便正确调用派生类的版本。
当在虚函数中使用默认参数时,默认参数的值通常在编译时确定,而虚拟函数调用是在运行时解析的。这就引发了潜在的问题,即默认参数的值在编译时确定,但虚拟函数的选择是在运行时进行的。当编译一个调用虚拟函数的地方时,编译器需要知道要调用哪个版本的函数,但默认参数的值在这时还不可知。因此虚函数会调用编译时的默认参数作为变量传入,这很显然和我们的目的相违背。
C++是一门开放的语言,你几乎可以做你想做的任何事情。编译器默认你已经知道了这个知识,不会禁止你在虚函数中使用默认参数。类似MSVC的编译器此时将抛出警告: Default arguments on virtual or override methods are prohibited.
5 解决方案
5.1 避免在虚拟和重写方法中使用默认参数
最简单的方法是避免在虚拟或重写方法中使用默认参数。
class Base {
public:
virtual void foo(int x) {
// 基类的虚拟函数实现
}
};
5.2 使用函数重载
如果你需要提供多个版本的函数,可以使用函数重载来实现,而不是在虚拟函数中使用默认参数。
class Base {
public:
virtual void foo() {
// 基类的虚拟函数实现
}
virtual void foo(int x) {
// 基类的虚拟函数实现
}
};
6 总结
在本文中,我们深入探讨了C++中的警告 “Default arguments on virtual or override methods are prohibited”,并解释了为什么在虚拟或重写方法中使用默认参数可能会导致问题。希望通过避免在虚拟或重写方法中使用默认参数,你能够提高代码的可维护性和稳定性,编写更健壮和高质量的C++代码。