1. 概念
1.1 什么是运算符重载
所谓重载,就是重新赋予新的含义。 函数重载就是对一个已有的函数赋予新的含义,使之实现新功能,因此,一个函数名就可以用来代表不同功能的函数,也就是”一名多用”。
运算符也可以重载。实际上,我们已经在不知不觉之中使用了运算符重载。例如,大 家都已习惯于用加法运算符”+”对整数、单精度数和双精度数进行加法运算,如5+8, 5.8 +3.67等,其实计算机对整数、单精度数和双精度数的加法操作过程是很不相同的, 但由于C++已经对运算符”+”进行了重载,所以就能适用于int, float, doUble类型的运算。
又如”<<“是C++的位运算中的位移运算符(左移),但在输出操作中又是与流对 象cout 配合使用的流插入运算符,”>>“也是位移运算符(右移),但在输入操作中又是与流对象 cin 配合使用的流提取运算符。这就是运算符重载(operator overloading)。C++系统对”<<“和”>>“进行了重载,用户在不同的场合下使用它们时,作用是不同 的。对”<<“和”>>“的重载处理是放在头文件stream中的。因此,如果要在程序中用”<< “和”>>”作流插入运算符和流提取运算符,必须在本文件模块中包含头文件stream(当然还应当包括”using namespace std“)。
1.2 运算符重载入门技术推演
- 为什么会用运算符重载机制
用复数类举例
//Complex c3 = c1 + c2;
//原因 Complex是用户自定义类型,编译器根本不知道如何进行加减
//编译器给提供了一种机制,让用户自己去完成,自定义类型的加减操作。。。。。
//这个机制就是运算符重载机制 - 运算符重载的本质是一个函数
#include "iostream"
using namespace std;
class Complex
{
public:
int a;
int b;
public:
Complex(int a=0,int b=0)
{
this->a=a;
this->b=b;
}
void printCom()
{
cout<<a<<" + "<<b<<"i"<<endl;
}
};
//定义了全局函数
Complex myAddd(Complex &c1,Complex &c2)
{
Complex tmp(c1.a+c2.a,c1.b+c2.b);
return tmp;
}
//
Complex operator+(Complex &c1,Complex &c2)
{
Complex tmp(c1.a+c2.a,c1.b+c2.b);
return tmp;
}
void main()
{
int a=0,b=0;
int c;
c=a+b; //1 基础数据类型 编译器已经知道了该如何运算
//a+bi 复数运算规则
Complex c1(1,2),c2(3,4);
Complex c3; //2 类 也是一种数据类型 用户自定义数据类型 C++编译器 是不知道如何进行运算的
//c3=c1+c2;
//3 C++编译器应该给我们程序员提供一种机制...
//让自定义数据类型 有机会 进行 运算符操作=====》运算符重载机制
//4 运算符重载机制
//步骤1
/*Complex c4 = myAddd(c1, c2);
c4.printCom();*/
//步骤2 //complex c4=c1+c2
/*Complex c4=operator+(c1,c2);
c4.printCom(); */
//步骤3
Complex c4=c1+c2;
c4.printCom();
//总结1: 运算符重载的本质 是 函数的调用
cout<<"hello..."<<endl;
system("pause");
return ;
}
2. 运符重载的限制
3. 运算符重载编程基础
//全局函数 完成 +操作符 重载
Complex operator+(Complex &c1, Complex &c2)
//类成员函数 完成 -操作符 重载
Complex operator-(Complex &c2)
3.1 运算符重载的两种方法
例如1:
/通过类成员函数完成-操作符重载
//函数声明 Complex operator-(Complex &c2)
//函数调用分析
//用类成员函数实现-运算符重载
Complex c4 = c1 - c2;
c4.printCom();
//c1.operator-(c2);
例如2:
//通过全局函数方法完成+操作符重载
//函数声明 Complex operator+(Complex &c1, Complex &c2)
//函数调用分析
int main()
{
Complex c1(1, 2), c2(3, 4);
//Complex c31 = operator+(c1, c2);
Complex c3 = c1 + c2;
c3.printCom();
}
例如3
//前置++操作符 用全局函数实现
Complex& operator++(Complex &c1)
{
c1.a ++;
c1.b ++;
return c1;
}
//调用方法
++c1 ; //=需要写出操作符重载函数原形
c1.printCom();
- 运算符重载函数名定义:
//首先承认操作符重载是一个函数 定义函数名 operator++
//分析函数参数 根据左右操作数的个数, operator++(Complex &c1)
//分析函数返回值 Complex& operator++(Complex &c1) 返回它自身
例如4
//4.1前置—操作符 成员函数实现
Complex& operator--()
{
this->a--;
this->b--;
return *this;
}
//4.2调用方法
--c1;
c1.printCom();
//4.3前置—运算符重载函数名定义
//c1.operator--()
例如5:
//5.1 //后置++ 操作符 用全局函数实现
Complex operator++(Complex &c1, int)
{
Complex tmp = c1;
c1.a++;
c1.b++;
return tmp;
}
//5.2 调用方法
c1 ++ ; //先使用 后++
//5.3 后置++运算符重载函数名定义
Complex operator++(Complex &c1, int) //函数占位参数 和 前置++ 相区别
例如6
//6.1 后置— 操作符 用类成员函数实现
Complex operator--(int)
{
Complex tmp = *this;
this->a--;
this->b--;
return tmp;
}
//6.2 调用方法
c1 ++ ; //先使用 后++
//6.3 后置--运算符重载函数名定义
Complex operator--(int) //函数占位参数 和 前置-- 相区别
前置和后置运算符总结:C++中通过一个占位参数(int(后置))来区分前置运算和后置运算
#include "iostream"
using namespace std;
class Complex
{
private:
int a;
int b;
friend Complex operator+(Complex &c1,Complex &c2);
friend Complex& operator++(Complex &c1);
friend Complex operator++(Complex &c1,int );
public:
Complex(int a=0,int b=0)
{
this->a=a;
this->b=b;
}
void printCom()
{
cout<<a<<" + "<<b<<"i"<<endl;
}
public:
Complex operator-(Complex &c2)
{
Complex tmp(this->a-c2.a,this->b-c2.b);
return tmp;
}
//前置--
Complex &operator--()
{
this->a--;
this->b--;
return *this;
}
//后置--
Complex operator--(int)
{
Complex tmp=*this;
this->a--;
this->b--;
return tmp;
}
};
//重载前置++
Complex& operator++(Complex &c1)
{
c1.a++;
c1.b++;
return c1;
}
//后置++
Complex operator++(Complex &c1,int)
{
//先使用,再让C1++
Complex tmp=c1;
/*return c1;*/
c1.a++;
c1.b++;
return tmp;
}
//全局函数法
Complex operator+(Complex &c1,Complex &c2)
{
Complex tmp(c1.a+c2.a,c1.b+c2.b);
return tmp;
}
void main()
{
Complex c1(1,2),c2(3,4);
Complex c3=c1 + c2;
c3.printCom();
//1 全局函数方法实现 + 运算符重载
//Complex operator+(Complex &c1,Complex &c2);
//2 成员函数方法实现加号运算符重载
//c1.operator-(this,c2);
//Complex operator-(Complex &c2)
Complex c4=c1 - c2;
c4.printCom();
//前置++操作符 全局
/*++c1;
Complex& operator++(Complex &c1);*/ //返回c1本身合适,返回一个引用
++c1;
c1.printCom();
//前置--操作符 成员
--c1;
c1.printCom();
//c1.operator--();
//Complex &operator--();
c1++;
c1.printCom();
//Complex operato r++(Complex &c1);
//后置--操作符 用成员函数实现
c1--;
c1.printCom();
//c1.operator--()
//Complex operator--();
cout<<"hello..."<<endl;
system("pause");
return ;
}
3.2 定义运算符重载函数名的步骤
全局函数、类成员函数方法实现运算符重载步骤
1)要承认操作符重载是一个函数,写出函数名称operator+ ()
2)根据操作数,写出函数参数
3)根据业务,完善函数返回值(看函数是返回引用 还是指针 元素),及实现函数业务
3.3 友元函数实现操作符重载的应用场景
1)友元函数和成员函数选择方法
- 当无法修改左操作数的类时,使用全局函数进行重载
- =, [], ()和->操作符只能通过成员函数进行重载
2)用友元函数 重载 << >>操作符
- istream 和 ostream 是 C++ 的预定义流类
- cin 是 istream 的对象,cout 是 ostream 的对象
- 运算符 << 由ostream 重载为插入操作,用于输出基本类型数据
- 运算符 >> 由 istream 重载为提取操作,用于输入基本类型数据
- 用友员函数重载 << 和 >> ,输出和输入用户自定义的数据类型
a)用全局函数方法实现 << 操作符
ostream& operator<<(ostream &out, Complex &c1)
{
//out<<"12345,生活真是苦"<<endl;
out<<c1.a<<" + "<<c1.b<<"i "<<endl;
return out;
}
//调用方法
cout<<c1;
//链式编程支持
cout<<c1<<"abcc";
//cout.operator<<(c1).operator<<("abcd");
//函数返回值充当左值 需要返回一个引用
b)类成员函数方法无法实现 << 操作符重载
//因拿到cout这个类的源码
//cout.operator<<(c1);
3)友元函数重载操作符使用注意点
a) 友员函数重载运算符常用于运算符的左右操作数类型不同的情况
b)其他
- 在第一个参数需要隐式转换的情形下,使用友员函数重载运算符是正确的选择
- 友员函数没有 this 指针,所需操作数都必须在参数表显式声明,很容易实现类型的隐式转换
- C++中不能用友员函数重载的运算符有 = () [] ->
#include "iostream"
using namespace std;
class Complex
{
private:
int a;
int b;
friend ostream& operator<<(ostream &out,Complex &c1);
public:
Complex(int a=0,int b=0)
{
this->a=a;
this->b=b;
}
void printCom()
{
cout<<a<<" + "<<b<<"i"<<endl;
}
public:
Complex operator-(Complex &c2)
{
Complex tmp(this->a-c2.a,this->b-c2.b);
return tmp;
}
//前置--
Complex &operator--()
{
this->a--;
this->b--;
return *this;
}
//后置--
Complex operator--(int)
{
Complex tmp=*this;
this->a--;
this->b--;
return tmp;
}
//重载 前置++
Complex& operator++()
{
this->a++;
this->b++;
return *this;
}
//后置++
Complex operator++(int)
{
//先使用,再让C1++
Complex tmp=*this;
/*return c1;*/
this->a++;
this->b++;
return tmp;
}
//+
Complex operator+(Complex &c2)
{
Complex tmp(this->a+c2.a,this->b+c2.b);
return tmp;
}
};
void main31()
{
Complex c1(1,2),c2(3,4);
Complex c3=c1 + c2;
c3.printCom();
//1 全局函数方法实现 + 运算符重载
//Complex operator+(Complex &c1,Complex &c2);
//2 成员函数方法实现加号运算符重载
//c1.operator-(this,c2);
//Complex operator-(Complex &c2)
Complex c4=c1 - c2;
c4.printCom();
//前置++操作符 全局
/*++c1;
Complex& operator++(Complex &c1);*/ //返回c1本身合适,返回一个引用
++c1;
c1.printCom();
//前置--操作符 成员
--c1;
c1.printCom();
//c1.operator--();
//Complex &operator--();
c1++;
c1.printCom();
//Complex operato r++(Complex &c1 );
//后置--操作符 用成员函数实现
c1--;
c1.printCom();
//c1.operator--()
//Complex operator--();
cout<<"hello..."<<endl;
system("pause");
return ;
}
//void operator<<(ostream &out,Complex &c1)
//{
// out<<"12345 csdfdf"<<endl;
// out<<c1.a<<"+"<<c1.b<<"i"<<endl;
//}
ostream& operator<<(ostream &out,Complex &c1)
{
out<<"12345 csdfdf"<<endl;
out<<c1.a<<"+ "<<c1.b<<"i"<<endl;
return out;
}
void main()
{
int a=10;
Complex c1(1,2),c2(3,4);
cout<<a<<endl;
//1
cout<<c1;
//2 ostream类中添加成员函数operator<<
//cout.operator<<(c1);
//输入输出流运算符重载必须用有函数
//cout.operator
//2
cout<<c1<<"aaaaddd";
//
cout<<"hello..."<<endl;
system("pause");
return ;
}
4. 运算符重载提高
4.1 运算符重载机制
4.2 重载赋值运算符=
- 赋值运算符重载用于对象数据的复制
- operator= 必须重载为成员函数
- 重载函数原型为:
类型 & 类名 :: operator= ( const 类名 & ) ;
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
//
class Name
{
public:
Name(const char *myp)
{
m_len = strlen(myp);
m_p =(char *) malloc(m_len + 1); //
strcpy(m_p, myp);
}
//Name obj2 = obj1;
//解决方案: 手工的编写拷贝构造函数 使用深copy
Name(const Name& obj1)
{
m_len = obj1.m_len;
m_p = (char *)malloc(m_len + 1);
strcpy(m_p, obj1.m_p);
}
//obj3 = obj1 ; // C++编译器提供的 等号操作 也属 浅拷贝
//obj3.operator=(obj1)
Name& operator=(Name &obj1)
{
//先释放旧的内存
if (this->m_p != NULL)
{
delete[] m_p;
m_len = 0;
}
//2 根据obj1分配内存大小
this->m_len = obj1.m_len;
this->m_p = new char [m_len+1];
//把obj1赋值
strcpy(m_p, obj1.m_p);
return *this;
}
~Name()
{
if (m_p != NULL)
{
free(m_p);
m_p = NULL;
m_len = 0;
}
}
protected:
private:
char *m_p ;
int m_len;
};
//对象析构的时候 出现coredump
void objplaymain()
{
Name obj1("abcdefg");
Name obj2 = obj1; //C++编译器提供的 默认的copy构造函数 浅拷贝
Name obj3("obj3");
obj3 = obj1; // C++编译器提供的 等号操作 也属 浅拷贝
//obj3.operator=(obj1)
//operato=(Name &obj1)
obj1 = obj2 = obj3;
//obj2.operator=(obj3);
//obj1 = void;
}
void main()
{
objplaymain();
cout<<"hello..."<<endl;
system("pause");
return ;
}
结论:
1 //先释放旧的内存
2 返回一个引用
3 =操作符 从右向左
4.3 重载数组下表运算符[]
重载[]和()运算符
- 运算符 [] 和 () 是二元运算符
- [] 和 () 只能用成员函数重载,不能用友元函数重载
重载下标运算符 []
[] 运算符用于访问数据对象的元素
重载格式 类型 类 :: operator[] ( 类型 ) ;
设 x 是类 X 的一个对象,则表达式
x [ y ]
可被解释为
x . operator [ ] ( y )
4.4 重载函数调用符 ()
() 运算符用于函数调用
- 重载格式 类型 类 :: operator() ( 表达式表 ) ;
例1
设 x 是类 X 的一个对象,则表达式
x ( arg1, arg2, … )
可被解释为
x . operator () (arg1, arg2, … )
例2:用重载()运算符实现数学函数的抽象
#include <iostream>
class F
{ public :
double operator ( ) ( double x , double y ) ;
} ;
double F :: operator ( ) ( double x , double y )
{ return x * x + y * y ; }
void main ( )
{
F f ;
f.getA();
cout << f ( 5.2 , 2.5 ) << endl ; // f . operator() (5.2, 2.5)
}
例3 用重载()运算符实现 pk 成员函数
#include <iostream.h>
class F
{
public :
double memFun ( double x , double y ) ;
};
double F :: memFun ( double x , double y )
{ return x * x + y * y ; }
void main ()
{
F f ;
cout << f.memFun ( 5.2 , 2.5 ) << endl ;
}
#include "iostream"
using namespace std;
class F
{
public:
int operator()(int a,int b)
{
return a*a + b*b;
}
};
class F2
{
public:
int MemFunc(int a,int b)
{
return a*a + b*b;
}
};
void main()
{
F f;
f(2,4);
cout<<f(2,4)<<endl;
F2 f2;
f2.MemFunc(2,4);
//7
//operator()(int a,int b)
cout<<"hello..."<<endl;
system("pause");
return ;
}
4.5 为什么不要重载&&和||操作符
理论知识:
- 1)&&和||是C++中非常特殊的操作符
- 2)&&和||内置实现了短路规则
- 3)操作符重载是靠函数重载来完成的
- 4)操作数作为函数参数传递
- 5)C++的函数参数都会被求值,无法实现短路规则
#include <cstdlib>
#include <iostream>
using namespace std;
class Test
{
int i;
public:
Test(int i)
{
this->i = i;
}
Test operator+ (const Test& obj)
{
Test ret(0);
cout<<"执行+号重载函数"<<endl;
ret.i = i + obj.i;
return ret;
}
bool operator&& (const Test& obj)
{
cout<<"执行&&重载函数"<<endl;
return i && obj.i;
}
};
// && 从左向右
void main71()
{
int a1 = 0;
int a2 = 1;
cout<<"注意:&&操作符的结合顺序是从左向右"<<endl;
if( a1 && (a1 + a2) )
{
cout<<"有一个是假,则不在执行下一个表达式的计算"<<endl;
}
Test t1 = 0;
Test t2 = 1;
//if( t1 && (t1 + t2) )
//t1 && t1.operator+(t2)
// t1.operator&&( t1.operator+(t2) )
//1 && || 重载他们 不会产生短路效果
if( (t1 + t2) && t1)
{
//t1.operator+(t2) && t1;
//(t1.operator+(t2)).operator&&(t1);
cout<<"两个函数都被执行了,而且是先执行了+"<<endl;
}
//2 && 运算符的结合性
// 两个逻辑与运算符 在一块的时候, 采去谈 运算符的结合性
// 从左到右 (t1 + t2) && t1 ; 运算结果 && t2)
if( (t1 + t2) && t1 && t2)
{
//t1.operator+(t2) && t1;
//(t1.operator+(t2)).operator&&(t1);
cout<<"两个函数都被执行了,而且是先执行了+"<<endl;
}
system("pause");
return ;
}