Pregunta ¿Qué es un operador sobrecargado en C ++?


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:

// 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 al leerlo todavía no los entiendo del todo. Solo necesito un ejemplo básico del punto del operador sobrecargado (que supongo que es el "CVector CVector :: operator + (CVector param)").

También hay 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 en la que estoy trabajando necesito sobrecargar a un operador ++ y a.

Gracias de antemano por la información y lo siento por la pregunta algo vaga, lamentablemente no estoy seguro de nada.


6
2018-03-09 03:39


origen


Respuestas:


La sobrecarga del 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 tu ejemplo para el Time sobrecarga del operador de clase para el + operador:

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

Con esa sobrecarga, ahora puede realizar operaciones adicionales en Time objetos de forma '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 estar sobrecargados, los que no pueden:

.  .*  :: and ?:

Puede llamar a la función directamente por su nombre, pero normalmente no (el punto de sobrecarga del operador es poder usar los operadores normalmente).

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

Una cosa adicional a tener en cuenta cuando se sobrecarga el ++ y -- operadores de incremento y decremento es que hay dos versiones de cada uno: el prefijo y los formularios de postfijo. La versión postfix de estos operadores toma un extra int parámetro (que se pasa a 0 y no tiene otro propósito que diferenciar entre los dos tipos de operador). El estándar 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 sean similares a las de los operadores integrados; al ser funciones más o menos normales, pueden hacer lo que quieran. 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).

Use la sobrecarga del operador con cuidado.


14
2018-03-09 03:48



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

Ahora, como cualquier otra función, puede sobrecargarla para decir que trabaja en otros tipos. En su ejemplo de vector, si sobrecarga operator + tomar CVector argumentos (es decir. operator +(CVector, CVector)), puedes decir:

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

Ya que ++ y -- son unarios (solo toman un argumento), para sobrecargarlos harías 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.


4
2018-03-09 03:45



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, ya que muestra la delegación al operador de asignación compuesta y el marcado apropiado de una función de miembro 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; }
};

2
2018-03-09 04:40



De tus comentarios anteriores, ¿no ves el punto 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 entera 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 antes, el objetivo de todo esto es hacer que el código (afortunadamente) sea más claro, como en el ejemplo de la 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, 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!

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

Espero que esto ayude...


2
2018-03-09 05:46



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

La idea aquí es que un operador no alguna cosa. Un operador sobrecargado no algo diferente.

Entonces, en este caso, el operador '+', normalmente usado para agregar dos números, está siendo "sobrecargado" para permitir agregar vectores o tiempo.

EDITAR: Agregar dos enteros está incorporado en c ++; el compilador entiende automáticamente lo que quieres decir cuando lo haces

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

Los objetos, por otro lado, pueden ser cualquier cosa, por lo que usar un '+' entre dos objetos no tiene ningún sentido inherente. Si tienes algo como

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

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


0
2018-03-09 03:41



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 de 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 del operador le permite hacer cosas que los diseñadores de C ++ no incorporaron al lenguaje, como usar 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 entiendas esto, los ejemplos de Wikipedia tendrán más sentido.


0
2018-03-09 03:50



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.

Sugiero que copies el primer ejemplo en un editor y juegues 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
2018-03-09 03:48



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

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, float) ...). 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 de postfijo e incrementos / decrementos de prefix que se vuelven muy importantes con la noción de iteradores y debe tener en cuenta que la sintaxis para sobrecargar estos dos tipos de operadores es diferente entre sí. Aquí es cómo puede sobrecargar estos operadores: Sobrecargar los operadores de incremento y decremento


0
2018-03-09 04:29



La respuesta aceptada por Michael Burr es bastante buena para explicar la técnica, pero 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 tiene un tipo para el cual existe un único significado comúnmente entendido para un operador en el dominio de su problema, proporcionar eso 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 su tipo 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. Siempre que operator== (y operator!=) puede darle una forma sencilla de hacerlo. Esto tiene la ventaja de cumplir una interfaz común que se puede usar 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 find Algoritmo implementado, depende de == siendo definido. 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 los tipos de objetos debe realizarse a través del .equals función miembro, 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 vale para ordenar. Si hay un orden bien definido (parcial) en el dominio de su clase, entonces proporcione operator< es una forma simple de implementar ese orden. El código será legible y su tipo será utilizable en todas las situaciones en las que se requiera un pedido parcial, como contenedores asociativos internos:

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 del operador se introdujo en el lenguaje es la del 'martillo dorado'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 usted sabe lo que está haciendo 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;

Que 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, necesitaría ir a la definición de la operación, y eso significa que sobrecargar al operador es menos legible que proporcionar una función:

MyVector c = append( a, b );

El conjunto de operadores que pueden estar sobrecargados no está restringido a los operadores aritméticos y relacionales. Puedes sobrecargar operator[] para indexar en un tipo, o operator() para crear un callable objeto que se puede usar como una función (estos se denominan 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 operator, puede sobrecargarse de maneras realmente sofisticadas para fines de metaprogramación, pero eso es probablemente mucho más complejo de lo que realmente te importa ahora.


0
2018-03-09 09:32



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 a la misma ubicación, y si modifica a, también afectará a b. Probablemente eso no es lo que quieres. Pero puedes 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 profunda.


0
2018-03-09 04:35