虚函数可以有默认参数吗?
如果我声明了一个基类(或接口类)并为其一个或多个参数指定了默认值,那么派生类是否必须指定相同的默认值,如果不是,哪些默认值将显示在派生类中?
附录:我也对如何在不同编译器中处理这个问题感兴趣,以及在这种情况下对“推荐”实践的任何意见。
虚拟可能有默认值。 基类中的默认值不会被派生类inheritance。
使用哪个默认值 – 即基类或派生类 – 由用于调用函数的静态types决定。 如果通过基类对象,指针或引用进行调用,则使用基类中指定的默认值。 相反,如果通过派生类对象调用指针或引用,则使用派生类中指定的默认值。 标准报价下面有一个例子来说明这一点。
一些编译器可能会做一些不同的事情,但这正是C ++ 03和C ++ 11标准所说的:
( 编辑 :C + + 11标准说完全一样的东西)
8.3.6.10:
虚拟函数调用(10.3)使用虚拟函数声明中的默认参数,该虚拟函数由指针或指向对象的引用的静态types确定。 派生类中的重写函数不会从它覆盖的函数中获取默认参数。 [例:
struct A { virtual void f(int a = 7); }; struct B : public A { void f(int a); }; void m() { B* pb = new B; A* pa = pb; pa->f(); //OK, calls pa->B::f(7) pb->f(); //error: wrong number of arguments for B::f() }
—end example]
编辑这里是一个示例程序来演示默认select。 为了简洁起见,我在这里使用的是struct
s而不是class
ES,除了默认的可见性之外, class
和struct
在几乎所有方面都是完全相同的。
#include <string> #include <sstream> #include <iostream> #include <iomanip> using std::stringstream; using std::string; using std::cout; using std::endl; struct Base { virtual string Speak(int n = 42); }; struct Der : public Base { string Speak(int n = 84); }; string Base::Speak(int n) { stringstream ss; ss << "Base " << n; return ss.str(); } string Der::Speak(int n) { stringstream ss; ss << "Der " << n; return ss.str(); } int main() { Base b1; Der d1; Base *pb1 = &b1, *pb2 = &d1; Der *pd1 = &d1; cout << pb1->Speak() << "\n" // Base 42 << pb2->Speak() << "\n" // Der 42 << pd1->Speak() << "\n" // Der 84 << endl; }
这个程序的输出(在MSVC10和GCC 4.4上)是:
Base 42 Der 42 Der 84
这是Herb Sutter早期的Guru of the Weekpost的主题之一。
他在这个问题上说的第一件事就是不要这样做。
更详细地说,是的,你可以指定不同的默认参数。 他们将不会像虚拟function一样工作。 在对象的dynamictypes上调用虚函数,而默认参数值则基于静态types。
特定
class A { virtual void foo(int i = 1) { cout << "A::foo" << i << endl; } }; class B: public A { virtual void foo(int i = 2) { cout << "B::foo" << i << endl; } }; void test() { A a; B b; A* ap = &b; a.foo(); b.foo(); ap->foo(); }
你应该得到A :: foo1 B :: foo2 B :: foo1
从其他答案中可以看出,这是一个复杂的主题。 而不是试图做这个或者理解它是做什么的(如果你现在要问,维护者将不得不在一年之后提出或查询)。
相反,使用默认参数在基类中创build一个公共的非虚函数。 然后调用一个私有或受保护的虚拟函数,它没有默认参数,并根据需要在子类中重写。 那么你不必担心它的工作细节,代码是非常明显的。
通过testing,你可以很好地弄清楚这个问题(也就是说,它是语言的一个主stream部分,大多数编译器几乎可以肯定它是正确的,除非你看到编译器之间的差异,它们的输出可以被认为是非常有效的)。
#include <iostream> struct base { virtual void x(int a=0) { std::cout << a; } virtual ~base() {} }; struct derived1 : base { void x(int a) { std:: cout << a; } }; struct derived2 : base { void x(int a = 1) { std::cout << a; } }; int main() { base *b[3]; b[0] = new base; b[1] = new derived1; b[2] = new derived2; for (int i=0; i<3; i++) { b[i]->x(); delete b[i]; } derived1 d; // dx(); // won't compile. derived2 d2; d2.x(); return 0; }
这是一个坏主意,因为你得到的默认参数将取决于对象的静态types,而调度到的virtual
函数将取决于dynamictypes。
也就是说,当你使用默认参数调用一个函数时,默认参数在编译时被replace,而不pipe函数是否为virtual
函数。
@cppcoder在他的[closed] 问题中提供了下面的例子:
struct A { virtual void display(int i = 5) { std::cout << "Base::" << i << "\n"; } }; struct B : public A { virtual void display(int i = 9) override { std::cout << "Derived::" << i << "\n"; } }; int main() { A * a = new B(); a->display(); A* aa = new A(); aa->display(); B* bb = new B(); bb->display(); }
其中产生以下输出:
Derived::5 Base::5 Derived::9
借助上面的解释,很容易看出原因。 在编译时,编译器用指针的静态types的成员函数replace默认参数,使其main
function等同于以下内容:
A * a = new B(); a->display(5); A* aa = new A(); aa->display(5); B* bb = new B(); bb->display(9);
正如其他答案已经详细说明,它的坏主意。 然而,因为没有人提到简单而有效的解决scheme,在这里它是:将您的参数转换为结构,然后你可以有结构成员的默认值!
所以,而不是,
//bad idea virtual method1(int x = 0, int y = 0, int z = 0)
做这个,
//good idea struct Param1 { int x = 0, y = 0, z = 0; }; virtual method1(const Param1& p)