¿Pueden las funciones virtuales tener parámetros predeterminados?

Si declaro una clase base (o clase de interfaz) y especifico un valor predeterminado para uno o más de sus parámetros, ¿las clases derivadas tienen que especificar los mismos valores predeterminados y, en caso negativo, qué valores predeterminados se manifestarán en las clases derivadas?

Adición: También estoy interesado en cómo se puede manejar esto en diferentes comstackdores y en cualquier entrada sobre la práctica “recomendada” en este escenario.

Virtuals puede tener valores predeterminados. Los valores predeterminados en la clase base no son heredados por las clases derivadas.

El valor predeterminado que se usa, es decir, la clase base ‘o una clase derivada’, se determina por el tipo estático utilizado para realizar la llamada a la función. Si llama a través de un objeto de clase base, puntero o referencia, se utiliza el valor predeterminado denotado en la clase base. Por el contrario, si llama a través de un objeto de clase derivado, un puntero o una referencia, se utilizan los valores predeterminados denotados en la clase derivada. Hay un ejemplo debajo de la cotización estándar que demuestra esto.

Algunos comstackdores pueden hacer algo diferente, pero esto es lo que dicen los estándares C ++ 03 y C ++ 11:

( EDITAR : El estándar C ++ 11 dice exactamente lo mismo)

8.3.6.10:

Una llamada de función virtual (10.3) usa los argumentos predeterminados en la statement de la función virtual determinada por el tipo estático del puntero o referencia que denota el objeto. Una función primordial en una clase derivada no adquiere argumentos predeterminados de la función que anula. [Ejemplo:

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] 

Editar Aquí hay un progtwig de muestra para demostrar qué valores predeterminados se recogen. Estoy usando struct s aquí en lugar de class simplemente por brevedad: class y struct son exactamente iguales en casi todos los aspectos, excepto en la visibilidad predeterminada.

 #include  #include  #include  #include  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; } 

La salida de este progtwig (en MSVC10 y GCC 4.4) es:

 Base 42 Der 42 Der 84 

Este fue el tema de uno de los primeros puestos de Guru of the Week de Herb Sutter.

Lo primero que dice sobre el tema es NO HACER ESO.

Con más detalle, sí, puede especificar diferentes parámetros predeterminados. No funcionarán de la misma manera que las funciones virtuales. Se invoca una función virtual en el tipo dynamic del objeto, mientras que los valores de parámetros predeterminados se basan en el tipo estático.

Dado

 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(); } 

deberías obtener A :: foo1 B :: foo2 B :: foo1

Como puede ver en las otras respuestas, este es un tema complicado. En lugar de tratar de hacer esto o entender lo que hace (si tiene que preguntar ahora, el mantenedor tendrá que pedirlo o buscarlo en un año a partir de ahora).

En su lugar, cree una función pública no virtual en la clase base con parámetros predeterminados. Luego llama a una función virtual privada o protegida que no tiene parámetros predeterminados y se reemplaza en clases secundarias según sea necesario. Entonces no tiene que preocuparse por los detalles de cómo funcionaría y el código es muy obvio.

Este es uno que probablemente pueda averiguar razonablemente bien mediante pruebas (es decir, es una parte del lenguaje lo suficientemente convencional como para que la mayoría de los comstackdores lo hagan correctamente y, a menos que vea diferencias entre los comstackdores, su resultado puede considerarse bastante autorizado).

 #include  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; } 

Esta es una mala idea, porque los argumentos predeterminados que obtenga dependerán del tipo estático del objeto, mientras que la función virtual enviada dependerá del tipo dynamic .

Es decir, cuando llama a una función con argumentos predeterminados, los argumentos predeterminados se sustituyen en tiempo de comstackción, independientemente de si la función es virtual o no.

@cppcoder ofreció el siguiente ejemplo en su pregunta [cerrada]:

 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(); } 

Que produce el siguiente resultado:

 Derived::5 Base::5 Derived::9 

Con la ayuda de la explicación anterior, es fácil ver por qué. En tiempo de comstackción, el comstackdor sustituye los argumentos predeterminados de las funciones miembro de los tipos estáticos de los punteros, haciendo que su función main equivalente a la siguiente:

  A * a = new B(); a->display(5); A* aa = new A(); aa->display(5); B* bb = new B(); bb->display(9); 

Como otras respuestas han detallado, es una mala idea. Sin embargo, dado que nadie menciona una solución simple y efectiva, aquí está: ¡Convierte tus parámetros a struct y luego puedes tener valores predeterminados para struct members!

Entonces, en lugar de,

 //bad idea virtual method1(int x = 0, int y = 0, int z = 0) 

hacer esto,

 //good idea struct Param1 { int x = 0, y = 0, z = 0; }; virtual method1(const Param1& p)