2010-01-13 19 views
23

(Editado de post original para cambiar "mensaje base" a "mensaje base const &")C++ Resumen de la sobrecarga de operadores de clase y la aplicación de interfaz pregunta

Hola a todos, Estoy muy nuevo en C++, así que ustedes, amigos espero puede ayudarme a "ver los errores de mi camino".

Tengo una jerarquía de mensajes y estoy tratando de usar una clase base abstracta para forzar una interfaz. En particular, quiero forzar cada mensaje derivado para proporcionar un operador < < sobrecargado.

Cuando intento hacer esto con algo como esto:

class BaseMessage 
{ 
public: 

// some non-pure virtual function declarations 
// some pure virtual function declarations 

virtual ostream& operator<<(ostream& stream, const BaseMessage& objectArg) = 0; 

} 

el compilador se queja de que

"Error: no se puede declarar el parámetro 'objectArg' sea de tipo abstracto 'mensaje base'

Creo que también hay problemas de "amigo" involucrados aquí, pero cuando traté de declararlo como:

virtual friend ostream& operator<<(ostream& stream, const BaseMessage objectArg) = 0;

el compilador añade un error Además

"error: funciones virtuales no pueden ser amigos"

¿Hay una manera de asegurar que todos mis (mensaje) que las clases derivadas proporcionan una "< <" operador ostream ?

Gracias Mucho,

Steve

+0

La respuesta de Nilolai es la mejor solución para lo que estás tratando de lograr. Sin embargo, el error específico que estaba obteniendo es porque está tratando de pasar un objeto BaseMessage por valor (el segundo argumento para su operador virtual <<). Esto no puede funcionar porque BaseMessage incluye una función virtual pura (el mismo operador virtual <<), por lo que no es posible construir una instancia de un BaseMessage para pasar por el valor. Observe que la versión del operador de Nilolai << toma su segundo argumento por referencia (será una clase derivada de Base). –

Respuesta

40

La convención común para esto es tener un operador de friend de salida a nivel de base y tienen que llamar a la función privada virtual:

class Base 
{ 
public: 

    /// don't forget this 
    virtual ~Base(); 

    /// std stream interface 
    friend std::ostream& operator<<(std::ostream& out, const Base& b) 
    { 
     b.Print(out); 
     return out; 
    } 

private: 

    /// derivation interface 
    virtual void Print(std::ostream&) const =0; 
}; 
+0

Esta es la única solución de trabajo que he visto en la página aquí: todos los demás están hablando de su culo :) (intente compilar su código de personas!) +1 – jkp

+0

Esto funcionó muy bien, gracias Nikolai puedo ver muchos lugares para usar este patrón. - Steve –

+0

No sabía que pudiera definir la función amigo directamente dentro de la declaración de clase así. ¡Bueno saber!Los libros de texto siempre pondrían la declaración del amigo dentro y la definición afuera. ¿La definición directamente dentro de una clase también funciona con una función de amigo 'swap'? –

3

clase abstracta no puede ser instanciada, por lo que hacer esto:

virtual ostream& operator<<(ostream& stream, const Base &objectArg) = 0; 

función virtual debe estar la función de miembro de instancia amigo mientras que la función es la función no miembro , por lo que no se puede declarar como virtual.

De manera similar, la función estática no puede ser virtual, ya que es un método de clase, no un método de instancia.

Mi sugerencia es:

class Base { 
public: 
virtual ostream& print (ostream& stream) const = 0; 
}; 


class Derived :public Base { 
public: 
virtual ostream& print (ostream& stream) const { //do something } 
}; 

ostream& operator <<(ostream& stream, const BaseMessage &objectArg) 
{ 
    return objectArg.print(stream); 
} 
0

El problema aquí es que "mensaje base objectArg" está diciendo que el objeto objectArg se debe pasar por valor.

Esto es imposible ya que ha hecho la clase abstracta con su llamada virtual pura. Un pase por referencia "BaseMessage & objectArg" o un pase por el puntero "BaseMessage * objectArg" haría que este error desaparezca.

Pasar por valor significa que crea una nueva instancia de la variable utilizando el constructor de copia. Como se ha asegurado de que es imposible crear una instancia de BaseMessage, no se puede hacer.

3

Los operadores de flujo como:

virtual ostream& operator<<(ostream& stream, const BaseMessage objectArg) = 0; 

simplemente no puede ser funciones miembro, y por lo tanto no pueden ser funciones virtuales. La razón de esto es que cuando se dice algo así como:

a << b; 

que realmente están diciendo

a.operator<<(b); 

En este caso, una es una corriente, no una instancia de su clase, por lo que el operador no puede ser un miembro de tu clase. Normalmente debe convertirlo en una función gratuita (no miembro), que accede a su instancia de clase a través de funciones de miembro adecuadas.

1

La declaración para el operador < <() es incorrecta. Para la versión binaria del op < <, usted no tiene que declarar el segundo parámetro - que se supone que es this si op < < es una función miembro de la clase:

virtual ostream& operator<<(ostream& stream) = 0;

Por otra parte, como lo mencionaron otros, los operadores de inserción de flujo deben ser funciones globales. Convertirlas en funciones miembro simplemente no funcionará.

También no es algo no relacionado con su pregunta, pero no es un problema. En su implementación original, pasó un objeto de clase base abstracta por valor, en lugar de por referencia o por puntero. Cuando haces esto, "cortas el objeto". Tu intención, estoy seguro, era pasar un puntero de clase base a un tipo polimórfico a la función, y luego tener los métodos de llamada de función polimórficamente. Por ejemplo, que estaba tratando de hacer algo similar a esto:

#include <cstdio> 
#include <string> 
#include <iostream> 
using namespace std; 

class Base 
{ 
public: 
    virtual void dump() 
    { 
     cout << "Base"; 
    }; 
}; 

class Der : public Base 
{ 
public: 
    void dump() 
    { 
     cout << "Der"; 
    }; 
}; 

void DumpIt(Base b) 
{ 
    b.dump(); 
} 


int main() 
{ 
    Der obj; 
    DumpIt(obj); 
    return 0; 

} 

... y esperando que la salida sea "Der" Pero, de hecho, la salida es "el punto" a causa de Object Slicing. Como la función DumpIt() toma el objeto Base por valor, se crea un nuevo objeto base temporal basado en el original. Con el fin de obtener la funcionalidad que se esperaba, se necesita pasar por referencia o por el puntero:

void DumpIt(Base & b) 
{ 
    b.dump(); 
} 

La salida de esta función es "Der".

Cuestiones relacionadas