2010-03-09 10 views
6

Me doy cuenta de que esta es una pregunta básica, pero he buscado en línea, he estado en cplusplus.com, he leído mi libro y parece que no entiendo el concepto de operadores sobrecargados. Un ejemplo específico de cplusplus.com es:¿Qué es un operador sobrecargado en C++?

// vectors: overloading operators example 
#include <iostream> 
using namespace std; 

class CVector { 
    public: 
    int x,y; 
    CVector() {}; 
    CVector (int,int); 
    CVector operator + (CVector); 
}; 

CVector::CVector (int a, int b) { 
    x = a; 
    y = b; 
} 

CVector CVector::operator+ (CVector param) { 
    CVector temp; 
    temp.x = x + param.x; 
    temp.y = y + param.y; 
    return (temp); 
} 

int main() { 
    CVector a (3,1); 
    CVector b (1,2); 
    CVector c; 
    c = a + b; 
    cout << c.x << "," << c.y; 
    return 0; 
} 

De http://www.cplusplus.com/doc/tutorial/classes2/ pero la lectura a través de él todavía no estoy entendiendo ellos en absoluto. Solo necesito un ejemplo básico del punto del operador sobrecargado (que supongo que es el "CVector CVector :: operator + (CVector param)").

Hay también este ejemplo de Wikipedia:

Time operator+(const Time& lhs, const Time& rhs) 
{ 
    Time temp = lhs; 
    temp.seconds += rhs.seconds; 
    if (temp.seconds >= 60) 
    { 
    temp.seconds -= 60; 
    temp.minutes++; 
    } 
    temp.minutes += rhs.minutes; 
    if (temp.minutes >= 60) 
    { 
    temp.minutes -= 60; 
    temp.hours++; 
    } 
    temp.hours += rhs.hours; 
    return temp; 
} 

De "http://en.wikipedia.org/wiki/Operator_overloading"

La asignación actual que estoy trabajando tengo que sobrecargar un ++ y un operador -.

Gracias de antemano por la información y disculpa por la pregunta algo vaga, desafortunadamente no estoy seguro de nada.

Respuesta

14

Sobrecarga de operador es la técnica que proporciona C++ para permitirle definir cómo los operadores en el lenguaje pueden aplicarse a objetos no construidos.

En ti ejemplo para la sobrecarga del operador Time clase para el + operador:

Time operator+(const Time& lhs, const Time& rhs); 

Con esa sobrecarga, ahora se puede llevar a cabo operaciones de suma en Time objetos de una manera 'natural':

Time t1 = some_time_initializer; 
Time t2 = some_other_time_initializer; 

Time t3 = t1 + t2; // calls operator+(t1, t2) 

La sobrecarga para un operador es solo una función con el nombre especial "operador" seguido del símbolo para el operador sobrecargado. La mayoría de los operadores pueden ser sobrecargados - los que no pueden son:

. .* :: and ?: 

puede llamar a la función directamente por su nombre, pero no suelen hacerlo (el punto de sobrecarga de operadores es ser capaz de utilizar los operadores normalmente).

La función sobrecargada que se llama está determinada por la resolución de sobrecarga normal de los argumentos para el operador; así es como el compilador sabe llamar al operator+() que usa los tipos de argumento Time del ejemplo anterior.

Una cosa adicional a tener en cuenta al sobrecargar los operadores de incremento y decremento ++ y -- es que hay dos versiones de cada uno: el prefijo y los formularios de postfijo. La versión de posfijo de estos operadores toma un parámetro adicional int (que se pasa a 0 y no tiene otra finalidad que diferenciar entre los dos tipos de operador). estándar de C++ tiene los siguientes ejemplos:

class X { 
public: 
    X& operator++();  //prefix ++a 
    X operator++(int); //postfix a++ 
}; 

class Y { }; 

Y& operator++(Y&);  //prefix ++b 
Y operator++(Y&, int); //postfix b++ 

También debe tener en cuenta que los operadores sobrecargados no tienen que realizar operaciones que son similares a la incorporada en los operadores - siendo más o funciones menos normales que pueden hacer lo que querer. Por ejemplo, la interfaz de flujo IO de la biblioteca estándar usa los operadores de desplazamiento para la salida y la entrada hacia/desde las transmisiones, lo que en realidad no es como el cambio de bit. Sin embargo, si intenta ser demasiado elegante con las sobrecargas de su operador, causará mucha confusión a las personas que intenten seguir su código (tal vez incluso a usted cuando vea su código más adelante).

Utilice la sobrecarga del operador con cuidado.

+3

+1 para los operadores que no puede sobrecargar. – FrustratedWithFormsDesigner

+0

Gracias por la asistencia ... lamentablemente, aunque entiendo mejor a los operadores sobrecargados, mi comprensión sobre cómo usarlos todavía no está lo suficientemente cerca como para querer usarlos. Pero al menos ahora entiendo mejor, ¡gracias de nuevo! – Jeff

+0

+1 Fantástica respuesta. En realidad, nunca se me ocurrió que el uso de '<<' and '>>' para la transmisión se debía en realidad a la sobrecarga del operador en la biblioteca estándar. Siempre pensé que era solo una de esas peculiaridades de C++ que estos operadores solo querían decir cosas diferentes según el contexto. – LeopardSkinPillBoxHat

0

El "operador" en este caso es el símbolo +.

La idea aquí es que un operador hace algo. Un operador sobrecargado hace algo diferente.

Por lo tanto, en este caso, el operador '+', normalmente utilizado para agregar dos números, se está "sobrecargando" para permitir la adición de vectores o tiempo.

EDITAR: Agregar dos enteros está incorporado en C++; el compilador entiende automáticamente lo que quiere decir cuando se hace

int x, y = 2, z = 2; 
x = y + z; 

objetos, por el contrario, puede ser cualquier cosa, así que usar un '+' entre dos objetos intrínsecamente no tiene ningún sentido. Si tiene algo como

Apple apple1, apple2, apple3; 
apple3 = apple1 + apple2; 

¿Qué significa cuando agrega dos objetos Apple juntos? Nada, hasta que sobrecargue el operador '+' y le diga al compilador qué es lo que quiere decir cuando agregue dos objetos Apple juntos.

+1

Desafortunadamente, esa explicación no tiene mucho sentido para mí ... el símbolo + parece bastante inútil, ¿no funcionaría bien este programa (utilizando el programa Time como ejemplo) sin él? ¿Por qué está ahí? Claro, entiendo que "es algo diferente" ... pero eso no explica qué está haciendo, por qué lo está haciendo, o qué sentido tiene. – Jeff

+0

Sí, funcionaría bien sin eso. Pero la sobrecarga del operador puede dar una sintaxis más "natural". ¿Preferirías escribir b * b-4 * a * c o restar (multiplicar (b, b), multiplicar (multiplicar (4, a), c))? – dan04

4

Un operador en C++ es solo una función con un nombre especial. Entonces en vez de decir Add(int,int) dices operator +(int,int).

Ahora como cualquier otra función, puede sobrecargarla para decir que funciona en otros tipos. (. es decir operator +(CVector, CVector)) en su ejemplo del vector, si sobrecarga operator + tomar CVector argumentos, a continuación, puede decir:

CVector a,b,res; 
res=a+b; 

Desde ++ y -- son unaria (que toman sólo un argumento), les sobrecargue que' d hacer como:

type operator ++(type p) 
{ 
    type res; 
    res.value++; 

    return res; 
} 

Dónde type es cualquier tipo que tiene un campo llamado value. Entiendes la idea.

+0

¿El operador no necesita ser miembro de uno de los tipos de operandos o al menos un amigo? –

+0

No, puede definirlos fuera de la clase si lo desea. Creo que la única excepción podría ser el operador =. – Maynza

+0

Eso es correcto, puede definir operadores globales fuera de clase. – Blindy

0

Un operador sería "+", "-" o "+ =". Estos realizan diferentes métodos en objetos existentes.Esto de hecho se reduce a una llamada a un método. Aparte de las llamadas a métodos normales, estas se ven mucho más naturales para un usuario humano. Escribir "1 + 2" simplemente se ve más normal y es más corto que "agregar (1,2)". Si sobrecarga un operador, cambia el método que ejecuta.

En su primer ejemplo, el método del operador "+" está sobrecargado, por lo que puede usarlo para agregar vectores.

Le sugiero que copie el primer ejemplo en un editor y juegue un poco con él. Una vez que comprenda lo que hace el código, mi sugerencia sería implementar la resta y multiplicación vectorial.

0

Un operador sobrecargado es cuando utiliza un operador para trabajar con tipos que C++ no admite de forma "nativa" para ese operador.

Por ejemplo, normalmente puede usar el operador "+" binario para agregar valores numéricos (flotantes, ints, dobles, etc.).También puede agregar un tipo entero a un puntero, por ejemplo:

char foo[] = "A few words"; 
char *p = &(foo[3]);  // Points to "e" 
char *q = foo + 3;  // Also points to "e" 

¡Pero eso es todo! No puedes hacer más de forma nativa con un operador "+" binario.

Sin embargo, la sobrecarga de operadores le permite hacer cosas que los diseñadores de C++ no construyó a la lengua - al igual que utilizar el operador + para concatenar cadenas - por ejemplo:

std::string a("A short"), b(" string."); 
std::string c = a + b; // c is "A short string." 

Una vez que envolver su cabeza alrededor de ese , los ejemplos de Wikipedia tendrán más sentido.

0

¡Antes de comenzar, hay muchos operadores por ahí! Aquí hay una lista de todos los operadores de C++: list.

Dicho esto, la sobrecarga del operador en C++ es una forma de hacer que un determinado operador se comporte de un modo particular para un objeto.

Por ejemplo, si usa los operadores de incremento/decremento (++ y -) en un objeto, el compilador no entenderá qué necesita incrementarse/decrementarse en el objeto porque no es un tipo primitivo (int , char, flotar ...). Debe definir el comportamiento apropiado para que el compilador comprenda lo que quiere decir. La sobrecarga del operador básicamente le dice al compilador lo que debe lograrse cuando los operadores de incremento/decremento se usan con el objeto.

Además, debe prestar atención al hecho de que hay incrementos/decrementos posfijo e incrementos/decrementos de prefijo que se vuelven muy importantes con la noción de iterators y debe tener en cuenta que la sintaxis para sobrecargar estos dos tipos de operadores es diferente de cada uno. Aquí se explica cómo puede sobrecargar estos operadores: Overloading the increment and decrement operators

0

Otro uso de la sobrecarga del operador, AFAIK exclusivo de C++, es la capacidad de sobrecargar el operador de asignación. Si usted tiene:

class CVector 
{ 
    // ... 
    private: 
     size_t capacity; 
     size_t length; 
     double* data; 
}; 

void func() 
{ 
    CVector a, b; 
    // ... 
    a = b; 
} 

Entonces a.data y b.data apuntarán en la misma ubicación, y si se modifica una, que afectará b también. Probablemente eso no es lo que quieres. Pero puede escribir:

CVector& CVector::operator=(const CVector& rhs) 
{ 
    delete[] data; 
    capacity = length = rhs.length; 
    data = new double[length]; 
    memcpy(data, rhs.data, length * sizeof(double)); 
    return (*this); 
} 

y obtener una copia en profundidad.

+0

'delete data' invoca un comportamiento indefinido, debe ser' delete [] data'. – fredoverflow

2

Lo que encontraste en esas referencias no son malos ejemplos de cuándo querrías la sobrecarga del operador (dando significado a la adición de vectores, por ejemplo), pero son códigos horribles cuando se trata de detalles.

Por ejemplo, esto es mucho más realista, mostrando delegar en el operador de asignación compuesto y adecuado marcado de un método constante:

class Vector2 
{ 
    double m_x, m_y; 
public: 
    Vector2(double x, double y) : m_x(x), m_y(y) {} 
    // Vector2(const Vector2& other) = default; 
    // Vector2& operator=(const Vector2& other) = default; 

    Vector2& operator+=(const Vector2& addend) { m_x += addend.m_x; m_y += addend.m_y; return *this; } 
    Vector2 operator+(const Vector2& addend) const { Vector2 sum(*this); return sum += addend; } 
}; 
-1

La sobrecarga de operadores le permite dar un significado propio para el operador. Por ejemplo, considere el siguiente fragmento de código:

char * str1 = "String1"; char * str2 = "String2"; char str3 [20];

str3 = str1 + str2;

Puede sobrecargar el operador "+" para concatenar dos cadenas. ¿No se ve esto más amigable con los programadores?

+0

¿Cómo es esa sobrecarga? Eso simplemente parece agregar (es decir, int str1 = 2; int str2 = 3, int str 3 = str1 + str2;), ¿cómo está una sobrecarga y la otra no? – Jeff

+0

If 'str1',' str2' y 'str3' donde' std :: string' que ejecutaría el operador 'std :: string + sobrecargado (std :: string const &, std :: string const &)'. Ser 'char *' es un error. No hay ningún operador que tome dos argumentos 'char *', e incluso si hubiera uno, no hay operador que asigne el resultado del operador anterior a la matriz (puede sobrecargar 'T operador + (char *, char *) 'con cualquier' T' que desee, pero no puede sobrecargar el operador de asignación para las matrices –

2

De sus comentarios anteriores, ¿no ve el sentido de toda esta sobrecarga del operador?

La sobrecarga del operador es simplemente 'azúcar sintáctico' que oculta una llamada a un método, y hace que el código sea algo más claro en muchos casos.

Considere una clase Entero simple que envuelve un int. Debería escribir agregar y otros métodos aritméticos, posiblemente incrementar y disminuir también, requiriendo una llamada a un método como my_int.add (5). ahora, cambiar el nombre del método add al operador + permite my_int + 5, que es el código más limpio e intuitivo. Pero lo único que está haciendo realmente es ocultar una llamada a su operador + (¿renombrar agregar?) Método.

Sin embargo, las cosas se vuelven un poco más complejas, ya que el operador + para los números es bien entendido por todos los que están por encima del 2 ° grado. Pero como en el ejemplo de cadena anterior, los operadores solo deberían aplicarse donde tienen un significado intuitivo. El ejemplo de las manzanas es un buen ejemplo de dónde NO sobrecargar los operadores. Pero aplicado para decir, una clase List, algo así como myList + anObject, debe entenderse intuitivamente como 'agregar un objeto a myList', de ahí el uso del operador +. Y operador '-' como 'Eliminación de la lista'.

Como dije anteriormente, el objetivo de todo esto es hacer que el código (con suerte) sea más claro, como en el ejemplo de lista, ¿cuál prefieres codificar? (¿y cuál le resulta más fácil de leer?) myList.add (anObject) o myList + onObject? Pero en el fondo, un método (su implementación de operator + o add) se está llamando de cualquier manera. Casi se puede pensar en el compilador reescribiendo el código: my_int + 5 se convertiría en my_int.operator + (5)

Todos los ejemplos dados, como las clases de tiempo y vector, todos tienen definiciones intuitivas para los operadores. Además del vector ... otra vez, es más fácil codificar (y leer) v1 = v2 + v3 que v1 = v2.add (v3). Aquí es donde toda la precaución es probable que lea con respecto a no ir por la borda con los operadores en sus clases, porque para la mayoría simplemente no tienen sentido. Pero, por supuesto, no hay nada que te impida ubicar a un operador & en una clase como Apple, ¡simplemente no esperes que otros sepan lo que hace sin ver el código!

'Sobrecarga' el operador simplemente significa que está suministrando al compilador otra definición para ese operador, aplicada a las instancias de su clase. Algo así como los métodos de sobrecarga, mismo nombre ... diferentes parámetros ...

Espero que esto ayude ...

+0

Tenga cuidado con las definiciones que pueden considerarse 'intuitivas'. ¿Qué significa 'a + b' para dos vectores? ¿Está agregando cada elemento emparejado? ¿Creará un nuevo vector a partir de la adición de los contenidos de 'b' en una copia de' a'? Solo sobrecargue a un operador cuando tenga un significado claro único. Se entiende bien la suma de dos números complejos y no puede haber una interpretación errónea. –

+0

La suma de vectores está tan bien definida como para los números complejos ... Además, debe devolver un nuevo vector de modo que (en 2D) el nuevo vector = (x1 + x2, y1 + y2) .... donde están los vectores originales (x1, y1) y (x2, y2) ... ¿Qué más podría significar? Solo mencioné Vect ors porque un ejemplo en el PO los usó ... –

+0

Después de reflexionar veo su pensamiento sobre un Vector en el sentido de estructura de datos, donde los ejemplos (y cómo lo usé) eran en el sentido matemático. Aún así, la razón por la cual la estructura de datos se llama Vector se relaciona con sus raíces matemáticas, por lo que las operaciones como V1 + V2 deben definirse de la misma manera, lo que significa que solo es válido si los elementos tienen un operador + definido sobre ellos. Entonces V1 + V2 = un nuevo Vecor V3 tal que V3_i = V1_i + V2_i, con 0 <= i <| Vector |. (todos los vectores deben ser de la misma longitud). Esto realmente equivale a un Vector N-dimensional, donde la física, etc. usa 3D. –

0

La respuesta aceptada por Michael Burr es bastante bueno en la explicación de la técnica, pero a partir de los comentarios parece que además del "cómo" estás interesado en el "por qué". Las razones principales para proporcionar sobrecargas al operador para un tipo dado son mejorar la legibilidad y proporcionar una interfaz requerida.

Si usted tiene un tipo para el que no hay una sola comúnmente se entiende que significa para un operador en el dominio de su problema, a continuación, siempre que como una sobrecarga del operador hace que el código sea más legible:

std::complex<double> a(1,2), b(3,4), c(5, 6); 
std::complex<double> d = a + b + c;   // compare to d = a.add(b).add(c); 
std::complex<double> e = (a + d) + (b + c); // e = a.add(d).add(b.add(c)); 

Si el tipo de tiene una propiedad dada que se expresará naturalmente con un operador, puede sobrecargar ese operador en particular para su tipo. Considere por ejemplo, que desea comparar sus objetos para la igualdad. Brindar operator== (y operator!=) puede darle una forma sencilla de hacerlo.Esto tiene la ventaja de cumplir con una interfaz común que se puede utilizar con algoritmos que dependen de la igualdad:

struct type { 
    type(int x) : value(x) {} 
    int value; 
}; 
bool operator==(type const & lhs, type const & rhs) 
    { return lhs.value == rhs.value; } 
bool operator!=(type const & lhs, type const & rhs) 
    { return !lhs == rhs; } 

std::vector<type> getObjects(); // creates and fills a vector 
int main() { 
    std::vector<type> objects = getObjects(); 
    type t(5); 
    std::find(objects.begin(), objects.end(), t); 
} 

Tenga en cuenta que cuando se implementa el algoritmo find, que depende de == se está definiendo. La implementación de find funcionará tanto con tipos primitivos como con cualquier tipo definido por el usuario que tenga un operador de igualdad definido. Hay una interfaz única común que tiene sentido. Compare eso con la versión de Java, donde la comparación de tipos de objetos debe realizarse a través de la función de miembro .equals, mientras que la comparación de tipos primitivos se puede hacer con ==. Al permitirle sobrecargar a los operadores, puede trabajar con tipos definidos por el usuario de la misma manera que con los tipos primitivos.

Lo mismo ocurre con los pedidos. Si hay un orden bien definido (parcial) en el dominio de su clase, proporcionar operator< es una forma sencilla de implementar ese orden. Código será legible, y su tipo será utilizable en todas las situaciones donde se requiere un orden parcial, como el interior de contenedores asociativos:

bool operator<(type const & lhs, type const & rhs) 
{ 
    return lhs < rhs; 
} 
std::map<type, int> m; // m will use the natural `operator<` order 

Un error común cuando la sobrecarga de operadores se introdujo en el lenguaje es el de la 'golden hammer 'Una vez que tienes un martillo dorado, todo parece un clavo, y se ha abusado de la sobrecarga del operador.

Es importante tener en cuenta que la razón de la sobrecarga en primer lugar es mejorar la legibilidad. La legibilidad solo se mejora si cuando un programador mira el código, las intenciones de cada operación son claras a primera vista, sin tener que leer las definiciones. Cuando vea que se están agregando dos números complejos como a + b, sabe lo que hace el código. Si la definición del operador no es natural (decide implementarla como si solo agregara la parte real), entonces el código será más difícil de leer que si hubiera proporcionado una función (miembro). Si el significado de la operación no está bien definido para su tipo sucede lo mismo:

MyVector a, b; 
MyVector c = a + b; 

¿Cuál es c? Es un vector donde cada elemento i es la suma de los elementos respectivos de a y b, o es un vector creado al concatenar los elementos de a antes de los elementos de b. Para entender el código, lo que tendría que ir a la definición de la operación, y eso significa que la sobrecarga del operador es menos legible que proporciona una función:

MyVector c = append(a, b); 

El conjunto de operadores que pueden ser sobrecargado no se limita a los operadores aritméticos y relacionales. Puede sobrecargar operator[] para indexar en un tipo o operator() para crear un exigible objeto que se puede utilizar como una función (éstos se llaman funtores) o que simplificará el uso de la clase:

class vector { 
public: 
    int operator[](int); 
}; 
vector v; 
std::cout << v[0] << std::endl; 

class matrix { 
public: 
    int operator()(int row, int column); 
     // operator[] cannot be overloaded with more than 1 argument 
}; 
matrix m; 
std::cout << m(3,4) << std::endl; 

Hay otros usos de la sobrecarga del operador. En particular, el operator, puede sobrecargarse de una manera realmente sofisticada con fines de metaprogramación, pero eso es probablemente mucho más complejo de lo que realmente le importa ahora.

Cuestiones relacionadas