2012-08-27 19 views
18

He creado una clase base abstracta, que tiene un método virtual puro con un argumento predeterminado.Buena práctica: Argumentos predeterminados para el método virtual puro

class Base { 
    ... 
    virtual someMethod(const SomeStruct& t = 0) = 0; 
    ... 
} 

class Derived : public Base { 
    ... 
    virtual someMethod(const SomeStruct& t = 0); 
    ... 
} 

Entonces, me gustaría saber si es una buena práctica establecer el argumento predeterminado en métodos virtuales puros y en general en métodos virtuales.

+3

Creo que se refería a const SomeStruct * t = 0? – marcinj

+4

@luskan: 'SomeStruct' podría ser implícitamente convertible de' 0'. –

+0

¿Qué quiere decir con "establecer el argumento predeterminado en virutal puro"? –

Respuesta

13

que a menudo desean utilizar ambos parámetros por defecto y función virtual como lo hace. Sin embargo, los otros han señalado con razón que esto genera ambigüedad y, en general, no es una buena idea. Hay una solución razonablemente simple, una que yo uso. Asigne a su función virtual un nombre diferente, haga que esté protegido y luego proporcione una función pública con parámetros predeterminados que lo llamen.

class Base { 
protected: 
    virtual void vSomeMethod(const SomeStruct& t) = 0; 
public: 
    void someMethod(const SomeStruc& t = 0) 
    { vSomeMethod(t); } 
} 

Las clases derivadas simplemente anulan vSomeMethod y no se preocupan en absoluto de los parámetros por defecto.

+4

En lugar de introducir otro nombre, "vSomeMethod", ¿por qué no utilizar la sobrecarga? Es decir. agregar una función pública de miembro no virtual 'void someMethod()' a 'Base' que llama a' someMethod (0); '. En el caso más general en el que desea una función de miembro virtual 'f (xn, ..., x1, x0)' que puede invocarse con valores de parámetro predeterminados 'n + 1',' xn = vn, ..., x1 = v1, x0 = v0', la sobrecarga virtual de 'f' podría no tener valores predeterminados, y podría haber una sobrecarga no virtual con' n' parámetros por defecto 'f (xn = vn, ..., x1 = v1) 'que llama a' f (xn, ..., x1, v0) '. – Ose

+2

@Ose La razón para usar una función de implementación designada por separado en lugar de sobrecargar el mismo nombre es porque cuando anula la implementación virtual en la clase secundaria, oculta el auxiliar no virtual en el elemento primario, lo que requiere que 'use' ese método en el niño –

6

No utilizar los parámetros por defecto en absoluto, si es posible, pero si lo hace, nunca se redefinen ellos (see the text for details)

Comprar tanto la efectiva C++ libros de Scott Meyers. No te arrepentirás.

+0

Estoy estableciendo los mismos valores predeterminados en ambos métodos, creo que su referencia es sobre la situación ambigua, cuando se pueden establecer diferentes valores como argumentos predeterminados. – deimus

+0

deimus, no importa realmente. Incluso si piensa, siempre usará los mismos valores que los argumentos predeterminados, es tan fácil hacer un error tipográfico o simplemente olvidarse de mencionar un argumento predeterminado. Y depurar este tipo de error hará que tu vida sea realmente miserable con cualquier código base adecuado. –

+2

@deimus no puede garantizar que alguien en el futuro no herede de su clase base y cambie el valor predeterminado. – juanchopanza

27

En realidad, su código es uno de los peores patrones de uso posibles para los parámetros predeterminados, ya que implica tanto el comportamiento de herencia como el comportamiento polimórfico. Apoyo un consejo para echar un vistazo a la sugerencia relacionada de Scott Meyers, pero aquí hay una breve descripción:

En caso de llamadas polimórficas, los parámetros predeterminados se utilizan de acuerdo con la declaración para tipo estático, no dinámico. Es lógico ya que el tiempo de ejecución no tiene idea de los parámetros predeterminados, pero rompe cualquier suposición razonable sobre el comportamiento polimórfico. Por ejemplo,

#include <cstdio> 

class Base 
{ 
     public: 
       virtual void f(int a = 1) 
       { 
         printf("Base::f(%d)\n", a); 
       } 
}; 

class Deriv : public Base 
{ 
     public: 
       virtual void f(int a = 2) 
       { 
         printf("Deriv::f(%d)\n", a); 
       } 
}; 

int main() 
{ 
     Base* a = new Deriv(); 
     a->f(); 
     delete a; 
     return 0; 
} 

rendimientos:

Deriv::f(1) 
+0

Buen ejemplo que muestra las dificultades asociadas con este enfoque. –

+1

Tu ejemplo me dejó alucinado, +1 – asimes

0

lo haría:

  • definir las funciones virtuales con el parámetro (sin valor predeterminado)
  • definir funciones virtuales no en la clase base, sin parámetro en todo, que llaman a la función virtual que pasa el parámetro predeterminado correcto

De esta manera, las clases derivadas no tienen que preocuparse por el valor predeterminado en absoluto.

0

Si desea que este código tenga sentido:

Base* p = new Derived; 
p->someMethod(); 

ya que el tipo estático de p es Base* es la firma de base que se utiliza en la llamada. El valor predeterminado se asigna y, al ser la función virtual, la llamada se redirige a Derivado.

Usted puede incluso tener que definen de manera diferente, si desea que su Derivado :: algunMetodo para recibir un valor diferente de Base* en lugar de Derived* ...

Lo importante es el documento de estas "relaciones" bueno, ya que la la mayoría de los programadores no los entenderán a partir de una simple lectura del código.

Por supuesto, si todo lo que no es adecuado en su contexto particular, generando más confusión que otra cosa, evite los parámetros predeterminados en las funciones virtuales, y use el auxiliar no virtual para llamarlos adecuadamente.

Pero teniendo en cuenta también que, desde el punto de vista del lector, un parámetro predeterminado es más explicativo que una función de sobrecarga que llama privadamente a otra con una repetición de parámetros no legible.

Cuestiones relacionadas