深入解析C++编程中的运算符重载

C++中预定义的运算符的操作对象只能是基本数据类型,实际上,对于很多用户自定义类型,也需要有类似的运算操作。例如:

class complex
{
 public:
 complex(double r=0.0,double I=0.0){real=r;imag=I;}
 void display();
 private:
 double real;
 double imag;
};
complex a(10,20),b(5,8);

“a+b”运算如何实现?这时候我们需要自己编写程序来说明“+”在作用于complex类对象时,该实现什么样的功能,这就是运算符重载。运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用于不同类型的数据导致不同类型的行为。
运算符重载的实质是函数重载。在实现过程中,首先把指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的实参,然后根据实参的类型来确定需要调用达标函数,这个过程爱编译过程中完成。

一、 运算符重载的规则
运算符重载规则如下:
①、 C++中的运算符除了少数几个之外,全部可以重载,而且只能重载C++中已有的运算符。
②、 重载之后运算符的优先级和结合性都不会改变。
③、 运算符重载是针对新类型数据的实际需要,对原有运算符进行适当的改造。一般来说,重载的功能应当与原有功能相类似,不能改变原运算符的操作对象个数,同时至少要有一个操作对象是自定义类型。
不能重载的运算符只有五个,它们是:成员运算符“.”、指针运算符“*”、作用域运算符“::”、“sizeof”、条件运算符“?:”。
运算符重载形式有两种,重载为类的成员函数和重载为类的友元函数。
运算符重载为类的成员函数的一般语法形式为:

函数类型 operator 运算符(形参表)
{
 函数体;
}

运算符重载为类的友元函数的一般语法形式为:

friend 函数类型 operator 运算符(形参表)
{
 函数体;
}

其中,函数类型就是运算结果类型;operator是定义运算符重载函数的关键字;运算符是重载的运算符名称。
当运算符重载为类的成员函数时,函数的参数个数比原来的操作个数要少一个;当重载为类的友元函数时,参数个数与原操作数个数相同。原因是重载为类的成员函数时,如果某个对象使用重载了的成员函数,自身的数据可以直接访问,就不需要再放在参数表中进行传递,少了的操作数就是该对象本身。而重载为友元函数时,友元函数对某个对象的数据进行操作,就必须通过该对象的名称来进行,因此使用到的参数都要进行传递,操作数的个数就不会有变化。
运算符重载的主要优点就是允许改变使用于系统内部的运算符的操作方式,以适应用户自定义类型的类似运算。

二、 运算符重载为成员函数
对于双目运算符B,如果要重载B为类的成员函数,使之能够实现表达式oprd1 B oprd2,其中oprd1为类A的对象,则应当把B重载为A类的成员函数,该函数只有一个形参,形参的类型是oprd2所属的类型。经过重载后,表达式oprd1 B oprd2 就相当于函数调用oprd1.operator B(oprd2).
对于前置单目运算符U,如“-”(负号)等,如果要重载U为类的成员函数,用来实现表达式U oprd,其中oprd为A类的对象,则U应当重载为A类的成员函数,函数没有形参。经过重载之后,表达式U oprd相当于函数调用oprd.operator U().
对于后置运算符“++”和“- -”,如果要将它们重载为类的成员函数,用来实现表达式oprd++或oprd--,其中oprd为A类的对象,那么运算符就应当重载为A类的成员函数,这时函数要带有一个整型形参。重载之后,表达式oprd++和oprd—就想当于函数调用oprd.operator++(0)和oprd.operator—(0);
运算符重载就是赋予已有的运算符多重含义。通过重新定义运算符,使它能够用于特定类的对象执行特定的功能,这便增强了C++语言的扩充能力。
 
1. 运算符重载的作用:
运算符重载允许C/C++的运算符在用户定义类型(类)上拥有一个用户定义的意义。重载的运算符是函数调用的语法修饰:

class Fred
{
public:
// ...
};

#if 0
// 没有算符重载:
Fred add(Fred, Fred);
Fred mul(Fred, Fred);

Fred f(Fred a, Fred b, Fred c)
{
return add(add(mul(a,b), mul(b,c)), mul(c,a)); // 哈哈,多可笑...
}
#else
// 有算符重载:
Fred operator+ (Fred, Fred);
Fred operator* (Fred, Fred);

Fred f(Fred a, Fred b, Fred c)
{
return a*b + b*c + c*a;
}
#endif

2. 可以用作重载的运算符:
算术运算符:+,-,*,/,%,++,--;
位操作运算符:&,|,~,^,<<,>>
逻辑运算符:!,&&,||;
比较运算符:<,>,>=,<=,==,!=;
赋值运算符:=,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=;
其他运算符:[],(),->,,(逗号运算符),new,delete,new[],delete[],->*。
下列运算符不允许重载:
.,.*,::,?:
 
3. 运算符重载后,优先级和结合性:
用户重载新定义运算符,不改变原运算符的优先级和结合性。这就是说,对运算符重载不改变运算符的优先级和结合性,并且运算符重载后,也不改变运算符的语法结构,即单目运算符只能重载为单目运算符,双目运算符只能重载双目运算符。
 
4. 编译程序如何选用哪一个运算符函数:
运算符重载实际是一个函数,所以运算符的重载实际上是函数的重载。编译程序对运算符重载的选择,遵循着函数重载的选择原则。当遇到不很明显的运算时,编译程序将去寻找参数相匹配的运算符函数。
 
5. 重载运算符有哪些限制:
(1) 不可臆造新的运算符。必须把重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中。
(2) 重载运算符坚持4个“不能改变”。
·不能改变运算符操作数的个数;
·不能改变运算符原有的优先级;
·不能改变运算符原有的结合性;
·不能改变运算符原有的语法结构。
 
6. 运算符重载时必须遵循哪些原则:
运算符重载可以使程序更加简洁,使表达式更加直观,增加可读性。但是,运算符重载使用不宜过多,否则会带来一定的麻烦。
(1) 重载运算符含义必须清楚。
(2) 重载运算符不能有二义性。
运算符重载函数的两种形式
运算符重载的函数一般地采用如下两种形式:成员函数形式和友元函数形式。这两种形式都可访问类中的私有成员。

三、例子
使用全局函数重载

#include <IOSTREAM.H> 

class A
{
public:
  A(int i):i(i){};
  void print(){cout<<i<<endl;}
  friend A operator + (A &a, A &b);//声明为友元
  friend A operator ++(A &a, int);
  friend A& operator ++(A &a);
  friend A& operator +=(A &a, A &b);
protected:
  int i;
private:
}; 

A operator + (A &a, A &b){//重载 a + b
  return A(a.i + b.i);
} 

A operator ++(A &a, int){//重载 a++
  return A(a.i++);
} 

A& operator ++(A &a){//重载 ++a
  a.i++;
  return a;
} 

A& operator +=(A &a, A &b){//重载 +=
  a.i += b.i;
  return a;
} 

void main(){
  A a(5);
  A b(3);
  (a += b).print();
}

使用成员函数重载

#include <IOSTREAM.H> 

class A
{
public:
  A(int i):i(i){};
  void print(){cout<<i<<endl;}
  A operator + (A &b);
  A& operator += (A &b);
  A operator ++(int);
  A& operator ++();
protected:
  int i;
private:
}; 

A A::operator + (A &b){//重载 +
  return A(i + b.i);
} 

A& A::operator+= (A &b){
  i += b.i;
  return *this;
} 

A A::operator++ (int){//i++
  return A(i++);
} 

A& A::operator ++(){//++i
  i++;
  return *this;
} 

void main(){
  A a = 2;
  A b = 3;
  (++a).print();
  (b++).print();
}
(0)

相关推荐

  • 深入理解C++移位运算符

    关于逻辑移位.算术移位可参见迅雷深大笔试题部分.的一道题. 以前看到C++标准上说,移位运算符(<<.>>)出界时的行为并不确定: The behavior is undefined if the right operand is negative, orgreater than or equal to the length in bits of the promoted left operand. 我当时也没有深究过这个问题.前几天有个网友来信问起这件事,我才发现,这和Intel

  • 详解C++中new运算符和delete运算符的使用

    C++ 支持使用 new 和 delete 运算符动态分配和释放对象.这些运算符为来自称为"自由存储"的池中的对象分配内存. new 运算符调用特殊函数 operator new,delete 运算符调用特殊函数 operator delete. 在 Visual C++ .NET 2002 中,标准 C++ 库中的 new 功能将支持 C++ 标准中指定的行为,如果内存分配失败,则会引发 std::bad_alloc 异常. 如果内存分配失败,C 运行库的 new 函数也将引发 st

  • 浅谈C++类型转化(运算符重载函数)和基本运算符重载(自增自减)

    类型转化(运算符重载函数) 用转换构造函数可以将一个指定类型的数据转换为类的对象.但是不能反过来将一个类的对象转换为一个其他类型的数据(例如将一个Complex类对象转换成double类型数据).在C++提供类型转换函数(type conversion function)来解决这个问题.类型转换函数的作用是将一个类的对象转换成另一类型的数据. 类型转换函数的一般形式为: operator 类型名( ){ 实现转换的语句 } 下面是简单实现.这时候,Base起了两方面的作用:类和数据类型.系统会在

  • C++中运算符 &和&&、|和|| 的详解及区别

    C++中运算符 &和&&.|和|| 的详解及区别 简介: &&是逻辑与运算符,||是逻辑或运算符,都是逻辑运算符,两边只能是bool类型 &与| 既可以进行逻辑运算,又可以进行位运算,两边既可以是bool类型,又可以是数值类型 区别: if (A && B) 如果 A 为 false ,整个表达式就为 false,不再计算 B 的值了. if (A & B) 如果 A 为 false ,整个表达式就为 false,但还要计算 B 的值

  • 详解C++中的函数调用和下标以及成员访问运算符的重载

    函数调用 使用括号调用的函数调用运算符是二元运算符. 语法 primary-expression ( expression-list ) 备注 在此上下文中,primary-expression 为第一个操作数,并且 expression-list(可能为参数的空列表)为第二个操作数.函数调用运算符用于需要大量参数的操作.这之所以有效,是因为 expression-list 是列表而非单一操作数.函数调用运算符必须是非静态成员函数. 函数调用运算符在重载时不会修改函数的调用方式:相反,它会在运算

  • C++ 中重载和运算符重载加号实现矩阵相加实例代码

     C++ 重载+运算符重载加号 实现矩阵相加 学习C++ 基础知识,这里实现简单的实例,记录下自己学习生活,很简单,大家一起看看吧! 实例代码: #include<iostream> #include<iomanip> using namespace std; class Complex { private: int i,j,n,a[2][3]; public: Complex(); Complex operator+(Complex &c); void display()

  • C/C++中运算符的优先级、运算符的结合性详解

    一.运算符的优先级 在C++ Primer一书中,对于运算符的优先级是这样描述的: Precedence specifies how the operands are grouped. It says nothing about the order in which the operands are evaluated. 意识是说优先级规定操作数的结合方式,但并未说明操作数的计算顺序.举个例子: 6+3*4+2 如果直接按照从左到右的计算次序得到的结果是:38,但是在C/C++中它的值为20.

  • 详解C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义,但是也没有显式的删除),编译器会自动的隐式生成一个拷贝构造函数和赋值运算符.但用户可以使用delete来指定不生成拷贝构造函数和赋值运算符,这样的对象就不能通过值传递,也不能进行赋值运算. class Person { public: Person(const Person& p) = dele

  • C++类型转换运算符的实例详解

    C++类型转换运算符的实例详解 C++中有4个类型转换运算符,使装换过程更规范 dynamic_cast; const_cast; static_cast; reinterpret_cast; 一.dynamic_cast 该运算符我在之前的文章中已经介绍过了 http://www.jb51.net/article/123252.htm 总之,该运算符的语法如下: dynamic_cast < type-name> (expression) 如果转型失败则返回0,即空指针. 该运算符的用途是,

  • C++中求余运算符(%)示例详解

    介绍: %是求余运算符,也叫模除运算符,用于求余数. %要求两个操作数均为整数(或可以隐式转换成整数的类型). 标准规定: 如果%左边的操作数为负数时,则模除的结果为负数或者0, 如果%左边的操作数为正数时,则模除的结构为正数或者0. 示例代码: #include<iostream> using namespace std; int main(){ char c = 253; int i =5 ; cout<<c%2<<endl; cout<<i%c<

随机推荐