什么是C ++中的重载运算符?

时间:2010-03-09 03:39:21

标签: c++ operator-overloading

我意识到这是一个基本问题,但我已经在线搜索过,去了cplusplus.com,通读了我的书,我似乎无法掌握重载运算符的概念。 cplusplus.com的一个具体例子是:

// 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;
}

http://www.cplusplus.com/doc/tutorial/classes2/开始,但通过阅读,我仍然根本不理解它们。我只需要一个重载运算符的基本示例(我假设它是“CVector CVector :: operator +(CVector param)”)。

维基百科也有这个例子:

 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;
 }

来自“http://en.wikipedia.org/wiki/Operator_overloading

我正在进行的当前任务我需要重载一个++和一个 - 运算符。

提前感谢您提供的信息,并对有些模糊的问题感到抱歉,不幸的是我根本不确定。

11 个答案:

答案 0 :(得分:14)

运算符重载是C ++提供的技术,可让您定义如何将语言中的运算符应用于非内置对象。

Time运算符的+类运算符重载的示例中:

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

通过该重载,您现在可以以“自然”方式对Time个对象执行添加操作:

Time t1 = some_time_initializer;
Time t2 = some_other_time_initializer;

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

操作符的重载只是一个函数,其特殊名称为“operator”,后跟运算符重载的符号。大多数运营商都可能超负荷运转 - 那些不能运营的运营商:

.  .*  :: and ?:

您可以直接通过名称调用该函数,但通常不会(运算符重载点是为了能够正常使用运算符)。

调用的重载函数由对运算符的参数的正常重载决策决定 - 这就是编译器如何调用使用上面示例中的operator+()参数类型的Time

当重载++--递增和递减运算符时,需要注意的另一件事是每个版本有两个版本 - 前缀和后缀形式。这些运算符的后缀版本需要额外的int参数(传递0并且除了区分两种类型的运算符之外没有其他目的)。 C ++标准有以下示例:

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++

您还应该知道,重载的运算符不必执行与内置运算符类似的操作 - 或多或少的正常函数,它们可以执行任何您想要的操作。例如,标准库的IO流接口使用移位运算符来输出和输入流 - 这实际上与位移不同。但是,如果你试图对操作符重载过于花哨,那么对于那些试图遵循你的代码的人来说会引起很大的困惑(甚至可能在你以后查看代码时也是如此)。

谨慎使用运算符重载。

答案 1 :(得分:4)

C ++中的运算符只是一个具有特殊名称的函数。所以,不要说Add(int,int),而是说operator +(int,int)

现在和其他任何函数一样,你可以重载它来说明其他类型的工作。在您的向量示例中,如果您重载operator +以获取CVector个参数(即。operator +(CVector, CVector)),则可以说:

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

由于++--是一元的(它们只接受一个参数),为了超载它们,你会这样做:

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

  return res;
}

type是任何具有value字段的类型。你明白了。

答案 2 :(得分:2)

你在这些参考文献中找到的内容并不是你想要运算符重载的例子(例如,赋予向量加法意义),但是当它涉及到细节时它们是可怕的代码。

例如,这更加真实,显示委托给复合赋值运算符并正确标记const成员函数:

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; }
};

答案 3 :(得分:2)

根据您上面的评论,您没有看到所有此操作符重载的重点?

运算符重载只是隐藏方法调用的“语法糖”,并且在许多情况下使代码更清晰。

考虑一个包装int的简单Integer类。您可以编写add和其他算术方法,也可以增加和减少,需要一个方法调用,如my_int.add(5)。现在将add方法重命名为operator +允许my_int + 5,这是更直观,更清晰,更清晰的代码。但它真正做的就是隐藏对运算符+(重命名为add?)方法的调用。

事情确实变得有点复杂,因为2号以上的每个人都很清楚数字的运算符+。但是在上面的字符串示例中,运算符通常只应用于具有直观含义的位置。苹果示例是不过载运算符的好例子。 但应用说,List类,类似于myList + anObject,应直观地理解为“将anObject添加到myList”,因此使用+运算符。运算符' - '表示'从列表中删除'。

正如我上面所说的,所有这一切的要点是让代码(希望)更清晰,就像在List示例中那样,你宁愿编码? (你觉得哪个更容易阅读?)myList.add(anObject)或myList + onObject?但是在后台,一种方法(你的operator +或add的实现)被调用。您几乎可以想到编译器重写代码:my_int + 5将成为my_int.operator +(5)

给出的所有示例,例如时间和矢量类,都具有运算符的直观定义。向量添加...再次,比v1 = v2.add(v3)更容易编码(和读取)v1 = v2 + v3。这是您在课堂上不会过度使用操作员的所有注意事项,因为对于大多数人来说,他们只是没有意义。但是当然没有什么可以阻止你把操作员和进入像Apple这样的课程,只是不要指望别人知道它的作用而不看它的代码!

'重载'运算符只是意味着您正在为编译器提供该运算符的另一个定义,应用于类的实例。更像是重载方法,同名......不同的参数......

希望这会有所帮助......

答案 4 :(得分:0)

在这种情况下,“运算符”是+符号。

这里的想法是运营商做某事。重载运算符会执行不同的

因此,在这种情况下,通常用于添加两个数字的'+'运算符正在“重载”以允许添加向量或时间。

编辑:在c ++中内置了两个整数;编译器自动理解你的意思

int x, y = 2, z = 2; 
x = y + z;
另一方面,

对象可以是任何东西,因此在两个对象之间使用“+”本身并没有任何意义。如果您有类似

的内容
Apple apple1, apple2, apple3;
apple3 = apple1 + apple2;

将两个Apple对象添加到一起时意味着什么?没什么,直到你重载'+'操作符并告诉编译器你将添加两个Apple对象放在一起时的含义。

答案 5 :(得分:0)

运算符为“+”,“ - ”或“+ =”。它们对现有对象执行不同的方法。这实际上归结为方法调用。除了普通方法调用之外,对于人类用户来说这些看起来更自然。写“1 + 2”看起来更正常,比“add(1,2)”更短。如果重载运算符,则更改它执行的方法。

在第一个示例中,“+”运算符的方法被重载,因此您可以将其用于向量添加。

我建议你将第一个例子复制到一个编辑器中并稍微玩一下。一旦你理解了代码的作用,我的建议就是实现向量减法和乘法。

答案 6 :(得分:0)

重载运算符是指您使用运算符处理C ++不“本机”支持该运算符的类型。

例如,您通常可以使用二进制“+”运算符来添加数值(浮点数,整数,双精度等)。您还可以向指针添加整数类型 - 例如:

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

但就是这样!你不能使用二进制“+”运算符进行原生操作。

但是,运算符重载允许你做C ++的设计者没有构建到语言中的东西 - 比如使用+运算符来连接字符串 - 例如:

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

一旦你绕过它,维基百科的例子会更有意义。

答案 7 :(得分:0)

在开始之前,那里有很多运营商!以下是所有C ++运算符的列表:list

有了这个说法,C ++中的运算符重载是一种使某个运算符以特定方式对象运行的方法。

例如,如果在对象上使用递增/递减运算符(++和 - ),编译器将无法理解对象中需要递增/递减的内容,因为它不是基本类型(int, char,float ...)。您必须为编译器定义适当的行为以理解您的意思。运算符重载基本上告诉编译器当增量/减量运算符与对象一起使用时必须完成什么。

另外,你必须注意有后缀递增/递减和前缀递增/递减的事实,这对于iterators的概念变得非常重要,你应该注意重载这两种类型的语法运营商彼此不同。以下是如何重载这些运算符:Overloading the increment and decrement operators

答案 8 :(得分:0)

运算符重载的另一个用途是C ++独有的AFAIK,它能够重载赋值运算符。如果你有:

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

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

然后a.data和b.data将指向同一位置,如果修改a,则也会影响b。这可能不是你想要的。但你可以写:

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);
}

并获得深层复制。

答案 9 :(得分:0)

迈克尔·伯尔接受的答案在解释这项技巧方面非常出色,但从评论中看,除了“如何”你对'为什么'感兴趣。为给定类型提供操作符重载的主要原因是提高可读性并提供所需的接口。

如果您的类型对于问题域中的运算符有一个通常理解的含义,那么将其作为运算符重载提供使代码更具可读性:

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) );

如果您的类型具有自然可以用运算符表示的给定属性,则可以为该类型重载该特定运算符。例如,考虑您要比较对象是否相等。提供operator==(和operator!=)可以为您提供一种简单易读的方法。这样做的好处是可以实现一个可以与依赖于相等的算法一起使用的通用接口:

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 );
}

请注意,实施find算法时,它取决于==的定义。 find的实现将与原始类型以及任何定义了相等运算符的用户定义类型一起使用。有一个共同的单一界面有意义。将其与Java版本进行比较,其中必须通过.equals成员函数执行对象类型的比较,而可以使用==来比较基元类型。通过允许您重载运算符,您可以使用与基本类型相同的方式处理用户定义的类型。

订购也是如此。如果您的类的域中有明确定义的(部分)订单,则提供operator<是实现该订单的简单方法。代码是可读的,您的类型将在需要部分订单的所有情况下使用,作为内部关联容器:

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

当操作符重载被引入语言时常见的陷阱是'golden hammer'一旦你有了金锤,一切看起来像钉子,操作符重载被滥用。

重要的是要注意,首先出现超载的原因是提高可读性。只有当程序员查看代码时,可读性才会得到改善,每个操作的意图乍看之下都是清晰的,而无需阅读定义。当您看到像a + b这样添加两个复杂数字时,您就会知道代码在做什么。如果运算符的定义不自然(您决定将其实现为仅添加它的实部),则代码将比您提供(成员)函数时更难阅读。如果没有为您的类型定义操作的含义,则会发生相同的情况:

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

什么是c?它是一个向量,其中每个元素iab中各个元素的总和,或者它是通过连接a的元素而创建的向量b的元素。要理解代码,您需要转到操作的定义,这意味着重载操作符的可读性低于提供函数:

MyVector c = append( a, b );

可以重载的运算符集不限于算术运算符和关系运算符。您可以重载operator[]以索引到一个类型,或operator()来创建一个可用作函数的可调用对象(这些被称为仿函数)或者将简化使用班级:

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;

运算符重载还有其他用途。特别是operator,可能会以非常奇特的方式超载以进行元编程,但这可能比你现在真正关心的要复杂得多。

答案 10 :(得分:-1)

操作员重载允许您为操作员赋予自己的意义。 例如,请考虑以下代码段:

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

str3 = str1 + str2;

您可以重载“+”运算符以连接两个字符串。这看起来不是程序员友好吗?