2009-07-17 12 views
30

¿Hay alguna diferencia entre definir un operador global que toma dos referencias para una clase y definir un operador miembro que toma solo el operando correcto?diferencia entre operador global y operador miembro

global:

class X 
{ 
public: 
    int value; 
}; 

bool operator==(X& left, X& right) 
{ 
    return left.value == right.value; 
}; 

de usuario:

class X 
{ 
    int value; 
    bool operator==(X& right) 
    { 
     return value == right.value; 
    }; 
} 
+2

Es una buena práctica para declarar argumentos no sólo de referencia, pero también const, siempre que estés seguro de que puedes. Los operadores de comparación pueden ser const y tomar referencias const seguramente. (Si nada más, es una promesa para el compilador que le permite hacer más optimizaciones). –

Respuesta

39

Una razón para usar operadores que no son miembros (generalmente declarados como amigos) es porque la izquierda lado de la mano es el que hace la operación. Obj::operator+ está muy bien para:

obj + 2 

pero para:

2 + obj 

no va a funcionar. Para esto, se necesita algo así como:

class Obj 
{ 
    friend Obj operator+(const Obj& lhs, int i); 
    friend Obj operator+(int i, const Obj& rhs); 
}; 

Obj operator+(const Obj& lhs, int i) { ... } 
Obj operator+(int i, const Obj& rhs) { ... } 
5

Hay por lo menos una diferencia. Un operador miembro está sujeto a modificadores de acceso y puede ser público, protegido o privado. Una variable miembro global no está sujeta a restricciones de modificador de acceso.

Esto es particularmente útil cuando se desea desactivar ciertos operadores como asignación

class Foo { 
    ... 
private: 
    Foo& operator=(const Foo&); 
}; 

Se podría lograr el mismo efecto por tener un único operador global declarada. Pero daría lugar a un error de enlace frente a un error de compilación (nipick: sí, daría lugar a un error de enlace dentro de Foo)

+0

Un único operador declarado global no enlazará (es decir, no habrá ningún programa), mientras que un operador miembro privado lo hará. Solo los usos ilegales del miembro privado causarán un error de compilación. ¿Correcto? –

6

Su opción más inteligente es que sea una función amigo.

Como mencionó JaredPar, la implementación global no puede acceder a miembros protegidos y privados de la clase, pero también hay un problema con la función miembro.

C++ permitirá las conversiones implícitas de los parámetros de la función, pero no una conversión implícita de this.

Si existen tipos que se pueden convertir a la clase X:

class Y 
{ 
public: 
    operator X(); // Y objects may be converted to X 
}; 


X x1, x2; 
Y y1, y2; 

Sólo algunas de las siguientes expresiones compilará con una función miembro.

x1 == x2; // Compiles with both implementations 
x1 == y1; // Compiles with both implementations 
y1 == x1; // ERROR! Member function can't convert this to type X 
y1 == y2; // ERROR! Member function can't convert this to type X 

La solución, para obtener lo mejor de ambos mundos, es implementar esto como un amigo:

class X 
{ 
    int value; 

public: 

    friend bool operator==(X& left, X& right) 
    { 
     return left.value == right.value; 
    }; 
}; 
+1

Eso no es un error, es una característica;) Odio las conversiones implícitas ... –

6

En suma a la respuesta por Codebender:

operadores miembros no están simétrico. El compilador no puede realizar el mismo número de operaciones con los operadores del lado izquierdo y derecho.

struct Example 
{ 
    Example(int value = 0) : value(value) {} 
    int value; 

    Example operator+(Example const & rhs); // option 1 
}; 
Example operator+(Example const & lhs, Example const & rhs); // option 2 
int main() 
{ 
    Example a(10); 
    Example b = 10 + a; 
} 

En el código anterior se producirá un error de compilación si el operador es una función miembro, mientras que funcionará como se espera si el operador es una función gratuita.

En general un patrón común es implementar los operadores que deben ser funciones miembro como miembros y el resto como libre de las funciones que el delegado de los operadores miembros:

class X 
{ 
public: 
    X& operator+=(X const & rhs); 
}; 
X operator+(X lhs, X const & rhs) 
{ 
    lhs += rhs; // lhs was passed by value so it is a copy 
    return lhs; 
} 
Cuestiones relacionadas