2012-03-21 16 views
10

Estoy tratando de sobrecargar operator<< como una función miembro. Funciona si simplemente hacer esto:No se puede sobrecargar el operador << como miembro función

friend ostream& operator<<(ostream& os, const MyClass& myClass); en mi archivo de cabecera y en mi archivo MyClass.cc:

ostream& operator<<(ostream& os, const MyClass& myClass) 
{ 
    return myClass.print(os); 
} 

Sin embargo, si trato de tomar la friend fuera y que sea una función miembro, entonces se queja de que operator<< solo puede tomar un argumento. ¿Por qué?

ostream& MyClass::operator<<(ostream& os, const MyClass& myClass) 
{ 
    return myClass.print(os); 
} 

leí en this question que no puede ser una función miembro, pero no sabe por qué?

Respuesta

35

Cuando se sobrecarga como una función miembro, a << b se interpreta como a.operator<<(b), por lo que solo toma un parámetro explícito (con this como parámetro oculto).

Dado que esto requiere que la sobrecarga sea parte de la clase utilizada como el operando de la izquierda, no es útil con ostream s normales y tal. Requeriría que su sobrecarga sea parte de la clase ostream, no es parte de su clase. Como no puede modificar ostream, no puede hacer eso. Eso deja solo la sobrecarga global como alternativa.

Hay, sin embargo, un patrón bastante ampliamente utilizada en el que sobrecargar el operador a nivel mundial, pero tienen que llamar a una función miembro:

class whatever { 
    // make this public, or the global overload a friend. 
    std::ostream &write(std::ostream &dest) const { 
     // write self to dest 
    } 
}; 

std::ostream &operator<<(std::ostream &os, whatever const &w) { 
    return w.write(os); 
} 

Esto es particularmente útil cuando/si quieres comportamiento polimórfico. No puedes hacer que el operador sobrecargado sea polimórfico, pero haces que la función miembro llame al virtual, por lo que actúa polimórficamente de todos modos.

Editar: a (espero) aclarar la situación, puede hacerlo de diferentes maneras.El primero y probablemente el más obvio es hacer público nuestro miembro write y hacer que el operador global lo llame. Dado que es pública, no tenemos que hacer nada especial para que el operador lo utilizan:

class myClass { 
public: 
    std::ostream &write(std::ostream &os) const { 
     // write stuff to stream 
     return os; 
    } 
}; 

std::ostream &operator<<(std::ostream &os, myClas const &m) { 
    // since `write` is public, we can call it without any problem. 
    return m.write(os); 
} 

Una segunda alternativa es hacer write privada, y declarar operator<< un amigo para darle acceso:

class myClass { 
    // Note this is private: 
    std::ostream &write(std::ostream &os) const { 
     // write stuff to stream 
     return os; 
    } 

    // since `write` is private, we declare `operator<<` a friend to give it access: 
    friend std::ostream &operator<<(std::ostream &, myClass const &); 
}; 

std::ostream &operator<<(std::ostream &os, myClas const &m) { 
    return m.write(os); 
} 

Hay una tercera posibilidad que es casi igual que el segundo:

class myClass { 
    // Note this is private: 
    std::ostream &write(std::ostream &os) const { 
     // write stuff to stream 
     return os; 
    } 

    // since `write` is private, we declare `operator<<` a friend to give it access. 
    // We also implement it right here inside the class definition though: 
    friend std::ostream &operator<<(std::ostream &os, myClas const &m) { 
     return m.write(os); 
    } 
}; 

Este tercer caso utiliza una regla bastante extraño (y poco conocido) en C + + llamado "inyección de nombre". El compilador sabe que una función friend no puede ser parte de la clase, por lo que en lugar de definir una función miembro, esto "inyecta" el nombre de esa función en el ámbito circundante (el alcance global, en este caso). Aunque operator<< se define dentro de la definición de clase, es no una función miembro en absoluto: es una función global.

+0

¿qué ocurre si el 'operator <<' se declara como público en la clase pero no se implementa en la clase como 'MyClass :: operator <<'? ¿Se requiere 'amigo'? –

+0

Interesante. Por lo tanto, puede declararse en la clase, implementarse como una función no miembro y no ser un amigo de la clase para seguir llamando a las funciones miembro públicas de esa clase. –

+0

@ 0A0D: No estoy seguro de seguir. En mi comentario anterior, "eso" se refería a la función miembro. ¿Estás hablando de otra cosa? –

9

Puede sobrecargar operator<< como una función miembro. Pero no puede escribir a un miembro operator<< que lleva un ostream en el lado izquierdo, y su clase en el lado derecho.

Cuando hace que algo sea una función miembro (no estática), hay un primer argumento implícito, el objeto que llama. operator<< es binario, por lo que solo se necesitan 2 argumentos. Si lo hace una función miembro, solo puede asignarle un parámetro, porque ya tiene uno (el objeto que llama). Y dado que ese objeto llamante es siempre el primer argumento, no es posible escribir el operador de salida como un miembro (no estático) (al menos, no en el formulario estándar para esa función en particular), porque para ese caso, el ostream necesita ser el primer argumento.

+1

Además, dado que el 'this' implícito será el primer argumento, simplemente no puede hacer que los operadores ostream sean miembros de la clase; el orden de los argumentos será al revés. –

+0

@JohnZwinck aunque podrías hacer que tu clase hiciera 'myclass >> cout', pero eso sería extraño ... especialmente con el encadenamiento. –

+0

¿Tiene que usarse para acceder al método de impresión en MyClass si se trata de una función que no es miembro? –

1

Piense en ello como esto: cuando se quiere transmitir a ostream, que está llamando la < < operador en el objeto de secuencia. Y no está permitido modificar directamente el método 'privado' de ostream. Entonces debes crear una versión sobrecargada y convertirla en un amigo.

Al crear su propio operador < < método en su clase, que está creando un método < < que operará en su clase, no un objeto ostream. Supongo que puede mantener un ostream internamente en su clase y transmitirlo, pero tendrá dificultades para escribir declaraciones encadenadas como esta: a << b << c.

+0

amigo se requiere incluso si mi método de impresión es público en MyClass? –

Cuestiones relacionadas