2012-01-26 28 views
10

Tengo una plantilla de clase donde algunos métodos se definen como virtuales para dar la posibilidad al usuario de mi clase de darles una implementación en su clase derivada. Tenga en cuenta que en mi clase de plantilla hay algunos métodos no virtuales que hacen uso del virtual (se llama a una clase virtual que debe devolver un valor en una clase no virtual).override C++ virtual method

¿Puede darme un ejemplo simple de un código correcto donde el método virtual de la clase padre debe devolver un valor (pero su implementación se proporciona en una clase hija) y el valor devuelto por el método virtual en la clase padre se usa en otros métodos de esa clase. Porque vi en alguna parte (por ejemplo aquí: Safely override C++ virtual functions) que esto puede causar algunos problemas y el método definido por el usuario anulará el método virtual de la clase padre.

Nota: programo con Code :: Blocks usando el compilador g ++.

EDIT: conforme a lo solicitado aquí un simple ejemplo de lo que quiero:

template<typename T> 
class parent { 
public: 
    // Public methods that user can call 
    int getSomething(T t); 
    void putSomething(T t, int x); 

    // public method that user should implement in his code 
    virtual float compute(T t) { } 

    // protected or private methods and attributes used internally by putSomething ... 
    float doComplexeThings(...); // this can call 
}; 

El cómputo método() debe ser aplicada por el usuario (la clase hija). Sin embargo, este método compute() es llamado por putSomething() y doComplexeThings() por ejemplo.

+0

Por lo que describes, es posible que no se necesite un método virtual (el polimorfismo estático puede funcionar para ti): difícil de decir según tu descripción. Por favor, publique el código de muestra que ha escrito para tener una mejor idea de lo que está tratando de hacer. – kfmfe04

+0

El ejemplo en la pregunta vinculada era sobre lo que crees que es una anulación, pero en realidad declara una sobrecarga debido a un error, p. falta un const y no se trata de valores devueltos. ¿Cuál crees que es el peligro, exactamente? – CashCow

+0

En el código de plantilla clásico, ni siquiera necesita declarar un 'cálculo de flotación virtual (T t)'. Simplemente utilícelo (llámelo) dentro de putSomething() y doComplexeThings() a través de 't.compute()', si tiene un control para la instancia. El compilador generará un error si su clase T no implementa el cálculo. De esta manera, el padre y T en realidad ni siquiera tienen que vivir en la misma jerarquía de herencia: es decir, T es una relación hijo de padre no es necesaria. Hacerlo también puede darle la oportunidad de darle a los padres un nombre más significativo (ya que la relación is-a no es necesaria). – kfmfe04

Respuesta

18

Sólo tiene que asegurarse de que los métodos tengan la misma firma (incluidos los modificadores const/mutable y los tipos de argumentos). Puede utilizar una definición virtual pura para provocar errores de compilación si no puede anular la función en una subclase.

class parent { 
public: 
    // pure virtual method must be provided in subclass 
    virtual void handle_event(int something) = 0; 
}; 

class child : public parent { 
public: 
    virtual void handle_event(int something) { 
    // new exciting code 
    } 
}; 

class incomplete_child : public parent { 
public: 
    virtual void handle_event(int something) const { 
    // does not override the pure virtual method 
    } 
}; 

int main() { 
    parent *p = new child(); 
    p->handle_event(1); // will call child::handle_event 
    parent *p = new incomplete_child(); // will not compile because handle_event 
             // was not correctly overridden 
} 
+0

* Los tipos de retorno covariantes * están permitidos para los métodos virtuales. –

+3

@als, solo se permite un pequeño detalle para * * tipos de retorno covariantes * ', los tipos covariantes solo se permiten si la función devuelve una referencia o un puntero. Sé que lo sabes, pero dejo este comentario para otros. –

+2

@mschneider El problema con su respuesta es que está forzando un cambio en la semántica solo para evitar un posible problema. En el caso original, la función estaba presente pero podría estar sobrecargada, en su solución propuesta, * debe * estar sobrecargada. Esto no soluciona los errores al sobrecargar una función existente, sino que * obliga * a las clases derivadas a sobrecargarse. –

20

Si puede utilizar C++ 11 características en su compilador entonces anula puede ser etiquetado como así con el override identificador especial:

float compute() override; 

La línea anterior en una clase derivada causará un compilador error ya que la función no anula una función miembro en la base (firma incorrecta, argumento faltante). Pero tenga en cuenta que esto debe hacerse en cada clase derivada, no es una solución que pueda imponer desde la clase base.

Desde la clase base solo puede forzar anular haciendo que la función sea puramente virtual, pero eso cambia la semántica. No evita los problemas al anular, sino que fuerza anulando en todos los casos. Evitaría este enfoque, y si debe seguirlo y hay una implementación sensata para el tipo base, haga que la función virtual y proporcionen una definición para que la implementación de las clases derivadas pueda simplemente llamar a las funciones el tipo base (es decir, fuerza la implementación, pero en los casos más simples solo reenviará la llamada al padre)

+0

+1 para una característica interesante de C++ 11 - útil para garantizar que su método realmente esté anulando algo – kfmfe04

0

Esta pregunta se hace en 2013. Es bastante antigua, pero encontré algo nuevo que no existe en las respuestas.

Tenemos que entender tres concepto es sobrecarga, sobrescribir, y ocultar.

Respuesta breve, desea sobrecargar la función de herencia de la clase base. Sin embargo, la sobrecarga es el mecanismo para agregar un comportamiento múltiple para la función que necesita todas estas funciones en la misma escala. Pero la función virtual está obviamente en la clase Base.

class A { 
public: 
    virtual void print() { 
    cout << id_ << std::endl; 
    } 
private: 
    string id_ = "A"; 
}; 

class B : A { 
public: 
    using A::print; 
    void print(string id) { 
    std::cout << id << std::endl; 
    } 
}; 

int main(int argc, char const *argv[]) { 
    /* code */ 
    A a; 
    a.print(); 
    B b; 
    b.print(); 
    b.print("B"); 
    return 0; 
} 

Añadir usando A :: impresión; en su clase de derivación hará el trabajo!

Aunque no me siento que es una buena idea ya que la filosofía detrás de la sobrecarga y herencia es diferente, puede que no es una buena idea para anidar juntos.