2008-10-25 9 views
6

Aquí está mi problema: Tengo un método virtual definido en un archivo .h al que quiero llamar en una clase que hereda de la clase base. Lamentablemente, el método en la clase derivada no se llama. ¿Hay una mejor manera de implementar lo que estoy tratando de hacer?Herencia en C++

#ifndef ofxBASE_SND_OBJ 
#define ofxBASE_SND_OBJ 

#include "ofConstants.h" 

class ofxBaseSndObj { 

public: 

    virtual string getType(){} 

    string key; 

}; 

#endif 

Aquí es mi clase de zumbido

#ifndef OFXSO_BUZZ 
#define OFXSO_BUZZ 

#include "ofxBaseSndObj.h" 

class ofxSOBuzz : public ofxBaseSndObj 
{ 
public: 
    string getType(); 
}; 

#endif 

ofxSOBuzz.cpp

string ofxSOBuzz::getType() 
{ 
    string s = string("ofxSOBuzz"); 
    printf(" ********* returning string type %s", s.c_str()); // doesn't get called! 
    return s; 
} 

Luego, en otra clase Trato de llamar de esta manera:

string ofxSndObj::createFilter(ofxBaseSndObj obj) 
{ 
    string str = obj.getType(); 
    if(str.compare("ofxSOBuzz") == 0) 
    { 
     printf(" all is well "); 
    } 
} 

En el método encima tengo que poder pasar en uno de m cualquier tipo de objeto que extienda el objeto ofxBaseSndObj. Cualquier sugerencia o puntero sería muy apreciada. ¡Gracias!

+1

Hasta donde sé, su encabezado (donde define ofxBaseSndObj) no compilará porque tiene una función con tipo de retorno no nulo sin declaración de retorno. ¿Cómo te las arreglas para ejecutar esto? – Arkadiy

+0

¡¡¡Sobre el momento de aceptar una respuesta como correcta !! Haga clic en una de las marcas de verificación junto a la respuesta que cree que responde mejor a la pregunta. –

Respuesta

25

cambiar esta línea:

string ofxSndObj::createFilter(ofxBaseSndObj obj) 

a

string ofxSndObj::createFilter(ofxBaseSndObj& obj) 

Lo que está haciendo es pasar por valor (pasando una copia).

Esto significa que está copiando el objeto a la función. Como la función no sabe qué tipo está realmente pasando, solo pasa el tipo definido en la declaración de la función y, por lo tanto, hace una copia de la clase base (esto se conoce como el problema slicing).

La solución es pasar por referencia.

Si no desea que la función modifique el objeto (tal vez por eso estaba pasando por valor para que no pueda alterar el original), pase una referencia constante.

class ofxBaseSndObj 
{ 
    public: 
     virtual string getType() const; 
     // If the method does not change the object mark it const 

     string key; 

}; 

string ofxSndObj::createFilter(ofxBaseSndObj const& obj) 
{ 
    // allowed to call this if getType() is a const 
    string str = obj.getType(); 

    if(str.compare("ofxSOBuzz") == 0) 
    { 
     printf(" all is well "); 
    } 
} 
10

Debe pasar la instancia para createFilter como un puntero (o referencia) al objeto. Usted es passing by value, y esto hace que el compilador copie el objeto derivado que usa como argumento en una instancia de la clase base. Cuando hace esto, pierdes el hecho de que originalmente era un tipo derivado.

Como está escrito, su código no debería compilarse ya que la declaración de ofxBaseSndObj :: getType no devuelve nada. ¿Quiso decir que esto es un método abstracto o devolver una cadena vacía?

Si lo hizo un método abstracto, entonces el compilador se quejaría de intentar crear una instancia de una clase abstracta en su método ofxSndObj :: createFilter.

+0

Sí, tienes razón, extraño. No debería compilar ... extraño. Estoy en gcc4.2, me pregunto si alguien más se ha topado con esto. –

2

Convertir el constructor de copia y el operador = privado es una forma efectiva de evitar que vuelva a ocurrir este error.

Por ejemplo:

class ofxBaseSndObj { 
public: 
    virtual string getType(){} 
    string key; 

private: 
    ofxBaseSndObj(const ofxBaseSndObj& rhs); 
    ofxBaseSndObj& operator=(const ofxBaseSndObj& rhs); 
}; 

Si no hay otra buena razón se debe utilizar C++ 's construido en RTTI.Luego puede usar el operador typeid. Mire la documentación de los compiladores para activarla si no está activada de manera predeterminada.

+0

Sí, voy a rediseñar para usar punteros y RTTI en su lugar. Quería evitar pasar por referencia (larga historia, la API tiene una extraña base de usuarios) pero creo que es la única forma en que funcionará y es la que tiene más sentido. ¡Gracias! –

-1

Usted podría utilizar dynamic_cast o TYPE_ID

1

Otros han abordado el problema de corte. Luego pregunta Ok, déjame decirte, sé que necesito hacer algo para determinar el tipo de base, pero ¿hay algo más elegante que hacer una búsqueda enum para determinar el tipo de objeto heredado?

Consultar y encender el tipo de objeto es un diseño deficiente que no aborda el enfoque de OO.

En lugar de

string ofxSndObj::createFilter(ofxBaseSndObj& obj) 
{ 
    string str = obj.getType(); 
    if(str.compare("ofxSOBuzz") == 0) 
    { 
     // do ofxSOBuzz - specific thing 
    } 
    else if(str.compare("some other derived class") == 0) 
    { 
     // do stuff for other derived classes 
    } 
     // etc... 
} 

hacen que el comportamiento interesante la función virtual:

class ofxBaseSndObj { 

public: 
    // get rid of getType() 
    virtual void HelpCreateFilter() = 0; 
}; 


string ofxSndObj::createFilter(ofxBaseSndObj& obj) 
{ 
    // Let the derived class do it's own specialized work. 
    // This function doesn't need to know what it is. 
    obj.HelpCreateFilter(); 
    // rest of filter creation 
} 

¿Por qué es mejor que la versión original? Porque ofxSndObj::createFilter no necesita modificar si las clases derivadas futuras de ofxBaseSndObj se agregan al sistema. Su versión necesita extenderse para cada nueva clase derivada. Si no está claro, intente publicar un poco más de código: no puedo decir a partir de su código o nombre de clase qué se supone que deben hacer estas funciones.

+0

Se ha ido, y se llevó su pregunta con él. – fizzer