1.操作符函数:

在特定条件下,编译器有能力把一个由操作数和操作符共同组成的表达式,解释为对

一个全局或成员函数的调用,该全局或成员函数被称为操作符函数.该全局或成员函数

被称为操作符函数.通过定义操作符函数,可以实现针对自定义类型的运算法则,并使之

与内置类型一样参与各种表达式运算.

2.首先我们先介绍下左值和右值,因为我们在运用运算符的时候要尽量和内置类型的一致性.

左值:有名的可以直接取地址的我们称之为左值,左值的特性是可以修改的.

右值:右值主要是一些临时变量,匿名变量,字符串字面值常量,字符常量;

表达式有的是左值有的是右值,例如+-*/运算返回的是右值,而赋值运算,复合赋值运算

返回的是左值.前++返回的是左值,后++返回的是右值.

类型转换(无论是显示的还是隐式的)都伴随着临时变量的产生.函数的返回值当返回的

不是引用的时候也是一个右值,但是一个引用的时候返回的是一个左值.

下面给出一个经典的全面左值和右值的示例:

#include <iostream>
int g = ;
using namespace std;
int foo(void)
{
return g;
} int& bar(void)
{
return g;
} void func(int& i)
{
cout << "func1" << endl;
}
void func(const int& i)
{
cout << "func2" << endl;
} int main(void)
{
int i; //左值,有名字可以取地址.
int* p = &i;
i = ; //可修改
//p = &10;这里的10是个右值,上面10赋值给i的时候产生的一个临时变量,是右值.所以10++也是错误的. // foo() = 10; 这里返回值也是临时变量是右值,是只读的属性,不能修改;++foo();也是错误的.    bar() = ; //这里返回的是一个左值引用,所以它是可以赋值的
cout << g << endl;
int a = ,b = ,c;
c = a + b;//a + b是右值.
//a + b = 10;是非法的,因为表达式的值也是用一个临时变量来存储的.是右值.
//无论是显示的还是隐式的类型转换,都会产生临时变量.
(a = b) = ; //赋值表达式返回的是左值.
++a = ; //前++表达式返回的是左值.
//a++ = 2000; 这里是不行的,后++运算符的表达式的值是右值.
//a++++;这也是不行的,对右值做++是不行的.
++++a;//这个是可以的.++a的返回值还是a本身.
cout << a << endl; //
char ch = ;
i = ch; //这里是可以的.
//int& r = ch;它会先把ch转换成一个临时变量,然后把这个临时;变量赋值给r,而引用是不能引用一个临时变量的.
//非常引用只能引用一个左值,不能是右值.
const int& r = ch; //常左引用又叫万能引用,它可以引用左值和右值.
func(ch);//这里它会调用参数是常属性的函数,因为它要类型转换,类型转换产生临时变量.
//所谓类型转换,无论是显示还是隐式的.都不是改变了原来的变量的类型,只能产生一个临时变量.
return ;
}

3.操作符重载的一般规则

a.C++不允许用户自己定义新的运算符,只能对已经存在的操作符进行重载.

b.C++大部分的运算符都可以重载,但是有一部分运算符是不能重载的主要有下面几种

.成员访问运算符; ::作用域解析运算符;.* 成员指针访问运算符;sizeof运算符;三目运算符;

.成员访问运算符和.*成员指针访问运算符不能重载的原因是为了保证成员访问的功能不能被改变;

::和sizeof运算符操作的对象是类型而不是一般的变量和表达式,不具备重载的特征.

c.重载不能改变操作的对象操作数的个数;

d.重载不能改变运算符的优先级;

e.重载函数的参数不能有默认的缺省参数值,因为它会改变运算符的操作数和前面的规则矛盾;

f.重载的参数不能全部都是C++的基本类型,因为这样会改变原有的用于标准的运算符的性质.

g.应当尽量使自定义的重载操作符和系统用于标准类型的运算符具有相似的功能;

h.运算符重载可以是类的成员函数,还可以是类的友元函数,还可以是普通的全局函数;

4.运算类双目操作符:+ - * /等   a.左右操作数均可以左值或右值;

b.表达式的值为右值.

c.成员函数形式:

class LEFT

{

const RESULT operator#(const RIGHT& right)const {}

};

全局函数的形式:

const RESULT operator#(const LEFT& left,const RIGHT& right){...}

#include <iostream>
using namespace std;
class Complex
{
public:
Complex(int r = ,int i = ):m_r(r),m_i(i){}
//运算符+-/*表达式的结果是右值,不能是引用.
/*参数用常引用的好处:
即避免了拷贝构造的开销,它又能接收常右操作数
且可以保证右操作数不被修改.
第三个const是可以支持常属性的左操作数.
因为非常对象依然可以调用常函数.
第一const为了追求语义上的一致性,使其返回值是一个
临时变量.不能被赋值.
*/
/*从做到右的三个const依次表示:
1.第一个const:返回常对象,禁止对加号表达式进行赋值.
2.第二个const:支持常右操作数,并且可以避免其被修改.
3.第三个const:支持常左操作数.
*/
const Complex operator+(const Complex& c) const
{
return Complex(m_r + c.m_r,m_i + c.m_i);
}
const Complex operator*(const Complex& c) const
{
return Complex(m_r*c.m_r,m_i*c.m_i);
}
const Complex operator/(const Complex& c) const
{
return Complex(m_r/c.m_r,m_i/c.m_i);
}
void print(void) const
{
cout << m_r << '+' << m_i << "i" << endl;
}
/*用全局函数来实现(友元)
const Complex operator-(const Complex& c) const
{
return Complex(m_r - c.m_r,m_i - c.m_i);
}
*/
private:
int m_r;
int m_i;
friend const Complex operator-(const Complex&,const Complex&);
};
const Complex operator-(const Complex& left,
const Complex& right)//这里没有const了,因为
//const是修饰this指针的,而全局函数是没有this指针的;
{
return Complex(left.m_r-right.m_r,left.m_i - right.m_i);
}
int main(void)
{
Complex c1(,),c2(,);
Complex c3 = c1 + c2;
//编译器翻译成Complex c3 = c1.operator+(c2);
c3.print();
Complex c4 = c1 + c2 + c3;
//Complex c4 = c1.operator+(c2).operator+(c3);
c4.print();
//(c1 + c2) = c3;如果加号运算符没有第一个const这里编译会通过.
c4 = c3 - c1;
//c4 = ::operator-(c3,c1);也可以处理成这样
c4.print();
c4 = c1*c2;
c4.print();
c4 = c2/c1;
c4.print();
return ;
}

5.赋值类操作运算符:=,+=,-=,*=,/=,^=,&=,|=等

a.左操作数必须是左值,右操作数可以是左值或者右值.

b.表达式的值为左值,返回自引用.是操作数本身,而非副本.

成员函数形式:

class LEFT

{

LEFT& operator#(const RIGHT& right){...}

}

全局函数形式:

LEFT& operator#(LEFT& left,const RIGHT& right){...}

#include <iostream>
using namespace std;
class Complex
{
public:
Complex(int r = ,int i = ):m_r(r),m_i(i){}
void print(void)const
{
cout << m_r << '+' << m_i << 'i' << endl;
}
//这里不能是const函数,因为左操作数要改变的.
Complex& operator+=(const Complex& c) //这里不能带const
{
m_r += c.m_r;
m_i += c.m_i;
return *this;
}
friend Complex& operator-=(Complex& left,const Complex& right)//友元函数没有this指针,也就没有常函数之说.
{
left.m_r -= right.m_r;
left.m_i -= right.m_i;
return left;
}
Complex& operator*=(const Complex& c)
{
m_r *= c.m_r;
m_i *= c.m_i;
return *this;
}
Complex& operator/=(const Complex& c)
{
m_r /= c.m_r;
m_i /= c.m_i;
}
private:
int m_r;
int m_i;
};
int main(void)
{
Complex c1(,),c2(,),c3(,);
(c1 += c2) = c3;
//c1.operator+=(c2).operator=(c3);
c1.print();
c3 -= c1;
c3.print();
//::operator-=(c3,c1);
c3 *= c1;
c3.print();
c3 /= c1;
c3.print();
return ;
}

6.运算类单目操作符:-,~,!等

a.操作数为左值或右值.

b.表达式的值为右值.

const RESULT operator#(void)const{...}

const RESULT operator#(const OPERAND& operand){...}

#include <iostream>
using namespace std;
class Integer
{
public:
Integer(int i = ):m_i(i){}
void print(void) const
{
cout << m_i << endl;
}
const Integer operator-(void) const
{
return Integer(-m_i);
}
friend const Integer operator~(const Integer& i)
{
return Integer(i.m_i * i.m_i);
}
const Integer operator!(void) const
{
return m_i?Integer():Integer();
}
private:
int m_i;
};
int main(void)
{
Integer i();
Integer j = -i;
//Integer j = i.operator-();
j.print();
j = ~i;
//j = ::operator~(i)求其平方.用~完成一个平方的效果.
j.print();
j = !i; //取反,表示真假之间的转换.
j.print();
return ;
}

7.前++前--后++后--运算符:

a.前自增减单目操作符:操作数为左值,表达式的值为左值,且为操作数本身(而非副本)

成员函数形式:

OPERAND& operator#(void){...}

全局函数形式:

OPERAND& operator#(OPERAND& operand){...}

b.后自增减单目操作符:操作数为左值,表达式的值为右值,且为自增减以前的值.

成员函数形式:

const OPERAND operator#(int){...}

全局函数形式:

const OPERAND operator#(OPERAND& operand,int){...}

8.输出操作符:<<

a.左操作数为左值形式的输出流(ostream)对象里面的成员,右操作数为左值或右值.

b.表达式的值为左值,且为左操作数本身(而非副本)

c.左操作数的类型为ostream,若以成员函数重载该操作符,就应该将其定义为ostream类的

成员,该类为标准库提供,无法添加新的成员,因此只能以全局函数的形式重载该操作符

ostream& operator<<(ostream& os,const RIGHT& right){...}

输入操作符:>>

a.左操作数为左值形式的输入流(istream)对象,右操作数为左值.

b.表达式的值为左值,且为左操作数本身(而非副本)

c.左操作数的类型为istream,若以成员函数形式重载该操作符,就应该将其定义为istream类的成员,

该类为标准库提供,无法添加新的成员,因此只能以全局函数的形式重载该操作符

istream& operator>>(istream& is,RIGHT& right){...}

#include <iostream>
using namespace std;
class Complex
{
public:
Complex(int r = ,int i = ):m_r(r),m_i(i){}
friend ostream& operator<<(ostream& os,const Complex& c)
{
return os << c.m_r << '+' << c.m_i << 'i';
}
friend istream& operator>>(istream& is,Complex& c)
{
return is >> c.m_r >> c.m_i;
}
private:
int m_r;
int m_i;
};
int main(void)
{
Complex c1(,),c2(,);
cout << c1 << ", " << c2 << endl;
cout << "Please input :" << endl;
cin >> c1 >> c2;
cout << c1 << ", " << c2 << endl;
return ;
}

9.下标运算符:[]

a.常用于在容器类型中以下标方式获取数据元素.

b.非常容器元素为左值,常容器的元素为右值.一个是非const成员,一个是const成员.并且必须定义为成员函数.

#include <iostream>
using namespace std;
class Array
{
public:
Array(size_t size):m_data(new int[size]),m_size(size){}
~Array(void)
{
if(m_data != NULL)
delete[] m_data;
m_data = NULL;
}
int& operator[] (size_t i)
{
return *(m_data + i);
}
//常版本
const int& operator[] (size_t i) const
{
return const_cast<Array&>(*this)[i];
}
private:
int* m_data;
size_t m_size;
};
int main(void)
{
Array a();
a[] = ; //a.operator[](0) = 13;
a[] = ;
a[] = ;
cout << a[] << ' ' << a[] << ' ' << a[] << endl;
const Array& r = a;
cout << r[] << ' ' << r[] << ' ' << endl;
return ;
}

10.小括号函数操作符()

a.如果一个类重载了函数操作符,那么该类的对象就可以被做函数来调用,其参数和返回值就是函数

操作符函数的参数和返回值.

b.参数的个数,类型以及返回值的类型,没有限制

c.唯一可以带有缺省参数的操作符函数

#include <iostream>
using namespace std;
class Square
{
public:
double operator()(double x) const
{
return x * x;
}
int operator()(int a,int b,int c = ) const
{
return a + b - c;
}
};
int main(void)
{
Square square;
cout << square(.) << endl;
//cout << square.operator()(13.) << endl;
cout << square(,,) << endl;
cout << square(,) << endl;
return ;
}

11.解引用和间接成员访问操作符:* 以及 ->

a.如果一个类重载了解引用和间接成员访问操作符,那么该类的对象就可以被当作指针来使用.

c/c++面试题(6)运算符重载详解的更多相关文章

  1. CPP-基础:运算符重载详解

    1.运算符重载定义: C++中预定义的运算符的操作对象只能是基本数据类型.但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作.这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功 ...

  2. C++运算符重载详解

    1.什么是运算符重载 运算符重载是一种函数重载. 运算符函数的格式:operatorop(argument-list)例如,operator+()重载+运算符.其中的op,必须是有效的C++运算符,如 ...

  3. C/C++对bool operator < (const p &a)const的认识,运算符重载详解(杂谈)

    下面来进行这段代码的分析: struct node {  //定义一个结构体node(节点)    int x;    int y;    int len;   //node中有3个成员变量x,y,l ...

  4. C++11运算符重载详解与向量类重载实例(<<,>>,+,-,*等)

    1. C++运算符重载介绍 C ++ 中预定义的运算符的操作对象只能是基本数据类型.但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作.这时就必须在C ++ 中重新定义这些运算符,赋予已 ...

  5. C++重载>>和<<(输入和输出运算符)详解

    转载:http://c.biancheng.net/view/2311.html 在C++中,标准库本身已经对左移运算符<<和右移运算符>>分别进行了重载,使其能够用于不同数据 ...

  6. Kotlin——最详细的操作符与操作符重载详解(上)

    本篇文章为大家详细的介绍Koltin特有的操作符重载.或许对于有编程经验的朋友来说,操作符这个词绝对不陌生,就算没有任何编辑基础的朋友,数学中的算数运算符也绝不陌生.例如(+.-.*./.>.& ...

  7. [java基础] 002 - 位运算符的详解和妙用

    一:位运算符详解 位运算符主要用来对操作数二进制的位进行运算.按位运算表示按每个二进制位(bit)进行计算,其操作数和运算结果都是整型值. Java 语言中的位运算符分为位逻辑运算符和位移运算符两类, ...

  8. 算法进阶面试题01——KMP算法详解、输出含两次原子串的最短串、判断T1是否包含T2子树、Manacher算法详解、使字符串成为最短回文串

    1.KMP算法详解与应用 子序列:可以连续可以不连续. 子数组/串:要连续 暴力方法:逐个位置比对. KMP:让前面的,指导后面. 概念建设: d的最长前缀与最长后缀的匹配长度为3.(前缀不能到最后一 ...

  9. C++函数重载详解

    我们在开瓶瓶罐罐的时候,经常会遭遇因各种瓶口规格不同而找不到合适的工具的尴尬.所以有时候就为了开个瓶,家里要备多种规格的开瓶器.同样是开个瓶子嘛,何必这么麻烦?于是有人发明了多功能开瓶器,不管啤酒瓶汽 ...

随机推荐

  1. SSI-Server Side Inclued

    SSI是指将内容发送到浏览器之前,可以使用“服务器端包含 (SSI)”指令将文本.图形或应用程序信息包含到网页中. IIS.Apache等主流web服务器都支持,cassini不支持.它并不经过asp ...

  2. [课程设计]Scrum 2.1 多鱼点餐系统开发进度(下单列表布局)

    [课程设计]Scrum 2.1 多鱼点餐系统开发进度(下单列表布局) 1.团队名称:重案组 2.团队目标:长期经营,积累客户充分准备,伺机而行 3.团队口号:矢志不渝,追求完美 4.团队选题:餐厅到店 ...

  3. easyui datagrid 仿ext—右键

    var createGridHeaderContextMenu = function(e, field) { e.preventDefault(); var grid = $(this);/* gri ...

  4. Normalize.css 初识

    一. 用来干嘛的 一个现代的.准备好了支持 HTML5 技术,并且要替代 CSS Reset 处理样式的理念. Normalize.css 使浏览器渲染所有元素更加一致,并且符合现代标准.它只是针对那 ...

  5. 【RabbitMQ】RabbitMQ在Windows的安装和简单的使用

    版本说明 使用当前版本:3.5.4 安装与启动 在官网上下载其Server二进制安装包,在Windows上的安装时简单的,与一般软件没什么区别. 安装前会提示你,还需要安装Erlang,并打开下载页面 ...

  6. c语言头文件中定义全局变量的问题

    c语言头文件中定义全局变量的问题 (转http://www.cnblogs.com/Sorean/) 先说一下,全局变量只能定义在 函数里面,任意函数,其他函数在使用的时候用extern声明.千万不要 ...

  7. 批量运行R包

    #批量运行包:all.pcg <- c("data.table","ggplot2","rmarkdown","tidyr& ...

  8. [poj2777] Count Color (线段树 + 位运算) (水题)

    发现自己越来越傻逼了.一道傻逼题搞了一晚上一直超时,凭啥子就我不能过??? 然后发现cin没关stdio同步... Description Chosen Problem Solving and Pro ...

  9. 配置cas

    在给tomcat配置好证书的基础上做一下操作(可以根据上一篇博客进行配置) 1.cas服务端配置(最后更改一下服务器tomcat的端口号) 第一步:下载cas-server-3.4.2.1-relea ...

  10. 利用win7系统自带的dos命令把笔记本无线网卡当无线路由器(无线AP发射器)

    利用win7系统自带的dos命令把笔记本无线网卡当无线路由器(无线AP发射器). 1.打开win7开始菜单,找到命令提示符选项,以管理员身份运行cmd.2.在命令行上输入:netsh wlan set ...