2008-10-23 25 views
12

quiero una clase padre virtual pura para llamar a una implementación niño de una función de este modo:clase de Padres C++ llamar a una función virtual niño

class parent 
{ 
    public: 
    void Read() { //read stuff } 
    virtual void Process() = 0; 
    parent() 
    { 
     Read(); 
     Process(); 
    } 
} 
class child : public parent 
{ 
    public: 
    virtual void Process() { //process stuff } 
    child() : parent() { } 
} 

int main() 
{ 
    child c; 
} 

esto debería funcionar, pero me da un error no enlazado:/Esto es usando VC++ 2k3

¿O no debería funcionar, estoy equivocado?

Respuesta

19

El título del siguiente artículo lo dice todo: Never Call Virtual Functions during Construction or Destruction.

+2

Correcto, pero algo corto. Al menos debe copiar la conclusión: "Cosas para recordar: no invoque funciones virtuales durante la construcción o la destrucción, porque tales llamadas nunca irán a una clase más derivada que la del constructor o destructor actualmente en ejecución". – paercebal

+0

Aún así, realmente creo que deberías pensar dos veces antes de mezclar la funcionalidad por herencia. En mi humilde opinión, esta pregunta muestra un flujo de diseño debajo de la superficie. – xtofl

2

Funciona en general, pero no para llamadas dentro del constructor de la clase base virtual pura. En el momento en que se construyó la clase base, la anulación de subclase no existe, por lo que no puede llamarla. Mientras lo llame una vez que se construye todo el objeto, debería funcionar.

2

Es porque su llamada está en el constructor. La clase derivada no será válida hasta que el constructor haya finalizado, por lo que el compilador tiene razón al evitarlo.

hay dos soluciones:

  1. hacer la llamada al procedimiento() en el constructor de la clase derivada
  2. definen un cuerpo de la función en blanco para el proceso como en el siguiente ejemplo:
class parent 
{ 
    public: 
    void Read() { //read stuff } 
    virtual void Process() { } 
    parent() 
    { 
     Read(); 
     Process(); 
    } 
} 
+0

Esto es peligroso, definiendo un espacio en blanco el cuerpo de la función en el padre y al llamarlo en su constructor dará como resultado que solo se ejecute la parte principal de Process() (es decir, nada). Probablemente quiera que la llamada se resuelva como una función virtual, eso es imposible en el constructor – Pieter

+0

De hecho: se llamará a la función en blanco. Esa no es una solución viable. – xtofl

4

Como alternativa, cree un método de fábrica para crear los objetos y haga que los constructores sean privados, el método de fábrica puede inicializar el objeto después de la construcción.

0

Usted necesita para envolver en el interior de un objeto que llama al método virtual después de que el objeto está totalmente construida:

class parent 
{ 
    public: 
    void Read() { /*read stuff*/ } 
    virtual void Process() = 0; 
    parent() 
    { 
     Read(); 
    } 
}; 

class child: public parent 
{ 
    public: 
    virtual void Process() { /*process stuff*/ } 
    child() : parent() { } 
}; 

template<typename T> 
class Processor 
{ 
    public: 
     Processor() 
      :processorObj() // Pass on any args here 
     { 
      processorObj.Process(); 
     } 
    private: 
     T processorObj; 

}; 




int main() 
{ 
    Processor<child> c; 
} 
2

Con un paso más usted podría introducir algún tipo de una función como

class parent 
{ 
    public: 
     void initialize() { 
      read(); 
      process(); 
     } 
} 
0

El problema superficial es que llama a una función virtual que aún no se conoce (los objetos se construyen de padres a hijos, así también lo son los vtables). Tu compilador te advirtió acerca de eso.

El problema esencial, que yo sepa, es que intenta reutilizar la funcionalidad por herencia. Esto es casi siempre una mala idea. Un problema de diseño, por así decirlo :)

Esencialmente, usted intenta crear instancias de un patrón Template Method, para separar el lo del cuando: leer primero algunos datos (de alguna manera), y luego procesarlo (en de alguna manera).

Esto probablemente funcionará mucho mejor con la agregación: dé la función de procesamiento al método de la plantilla para que se llame en el momento adecuado. Tal vez incluso puedas hacer lo mismo con la funcionalidad de lectura.

La agregación se puede hacer de dos maneras:

  1. Uso de las funciones virtuales (es decir,Runtime Encuadernación)
  2. Uso de plantillas (es decir, tiempo de compilación Encuadernación)

Ejemplo 1: tiempo de ejecución de unión

class Data {}; 
class IReader { public: virtual Data read()   = 0; }; 
class IProcessor { public: virtual void process(Data& d) = 0; }; 

class ReadNProcess { 
public: 
    ReadNProcess(IReader& reader, IProcessor processor){ 
     processor.process(reader.read()); 
    } 
}; 

Ejemplo 2: tiempo de compilación obligatorio

template< typename Reader, typename Writer > // definitely could use concepts here :) 
class ReadNProcess { 
public: 
    ReadNProcess(Reader& r, Processor& p) { 
     p.process(r.read()); 
    } 
}; 
Cuestiones relacionadas