小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
一、运算符重载
1.1 基本概念
运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。运算符重载(operator overloading)只是一种”语法上的方便”,也就是它只是另一种函数调用的方式。在C++中,可以定义一个处理类的新运算符。这种定义很像一个普通的函数定义,只是函数的名字由关键字operator
及其紧跟的运算符组成。差别仅此而已。它像任何其他函数一样也是一个函数,当编译器遇到适当的模式时,就会调用这个函数。
-
运算符重载:给运算符重新赋予新的含义,原来的功能保留。
注意:功能保留并不是说运算符有各种功能,而是需要我们自己实现原功能的逻辑运算符重载的本质,就是函数函数重载 例:
operator=
-
重载的方法:定义一个运算符重载的函数,在需要执行对应的运算逻辑时,编译器自动找到对应的运算符重载函数
-
运算符重载函数的参数类型或个数由 运算符自身 决定
-
由运算符左侧的操作数来调用运算符重载函数
例: operator= A = B; //从编译器的角度 A.operator+(B) ----左调右参
-
当没有定义运算符重载函数时,不能直接对==类的对象==进行相关操作
注意:
- 运算符重载可以在全局实现,但是定义成全局的,对类的私有的成员访问不方便如果非要全局实现,最好用 ==
friend
== - 成员函数版本和全局函数版本只能实现一个(因为调用时,会有歧义)
2.2 可重载的运算符和不可重载的运算符
2.2.1 运算符重载的格式
2.2.1.1 双目运算符的重载
+ - * / % & | ^ > < == != >= <=
表达式:
L # R
L:做操作数
R:右操作数
#:运算符
左操作数L : 既可以是一个左值 也可以是一个右值
右操作数L : 既可以是一个左值 也可以是一个右值
表达式的结果:只能是右值
成员函数版本:
+ - * / % & | ^
const 类名 operator#(const 类名 &R)const;
> < == != >= <=
const bool operator#(const 类名 &R)const;
全局函数版本:
+ - * / % & | ^
friend const 类名 operator#(const 类名 &L, const 类名 &R);
> < == != >= <=
friend const bool operator#(const 类名 &L, const 类名 &R);
2.2.1.2 赋值类运算符的重载
= += -= *= /= %= &= |= ^=
表达式:L # R
左操作数:只能是左值
右操作数:既可以是一个左值 也可以是一个右值
表达式的结果:做操作数自身
成员函数版本:
类名 &operator#(const 类名 &R);
全局函数版本:
friend 类名 &operator#(类名 &L,const 类名 &R);
2.2.1.3 单目运算符的重载
-(负) !(非) ~(取反)
表达式:#R
操作数: 既可以是一个左值 也可以是一个右值
表达式的结果:只能是右值
成员函数版本:
const 类名 operator#(void)const{}
全局函数版本:
friend const 类名 operator#(const 类名 &R){}
2.2.1.4 自增/自减运算符重载
前自增/前自减
++i --i
表达式: #R
操作数: 只能是左值
表达式的结果: 左值
成员函数版本:
类名 &operator#(void){}
全局函数版本:
friend 类名 &operator#(类名 &R){}
后自增/后自减
i++ i--
表达式: R#
操作数: 只能是左值
表达式的结果: 右值
成员函数版本:
const 类名 operator#(int){}
全局函数版本:
friend const 类名 operator#(类名 &R,int){}
2.2.1.5 插入和提取运算符重载
<<(提取) >>(插入)
需要用到 istream ostream 两个类
namespace std{
istream cin;
ostream cout;
}
插入和提取运算符的重载 是能是全局版本
<< 运算符的重载
cout<<a; ---- cout.operator<<()
friend ostream &operator<<(ostream &x,const 类名 &R);
#include <iostream>
using namespace std;
class Time{
private:
int hour;
int min;
int sec;
public:
Time(){}
Time(int h,int m,int s):hour(h),min(m),sec(s){}
void show(){
cout<<hour<<":"<<min<<":"<<sec<<endl;
}
friend const Time operator-(const Time &L, const Time &R);
};
const Time operator-(const Time &L, const Time &R){
Time temp;
temp.hour = L.hour - R.hour;
temp.min = L.min - R.min;
temp.sec = L.sec - R.sec;
return temp;
}
int main(){
Time t1(20,20,20);
Time t2(10,15,3);
Time t3 = t1 - t2;
t3.show();
return 0;
}
2.2.2 不能重载重载的运算符
.
取成员运算符::
作用域限定符?:
三目运算符- sizeof
.*
成员指针运算符#
预处理符号
2.3 当类的成员是公有时编写运算符重载函数
#include <iostream>
using namespace std;
class Goods{
public:
Goods(){};
Goods(string name, int money, string date)
{
this->name = name;
this->money = money;
this->date = date;
}
string name;
int money;
string date;
};
void sumMoney(Goods &obj1, Goods &obj2)
{
int sum = obj1.money + obj2.money;
cout << "一共话费" << sum << "元" << endl;
}
//+ -运算符的重载
int operator+(Goods &obj1, Goods &obj2)
{
int sum = obj1.money + obj2.money;
cout << "一共话费" << sum << "元" << endl;
return sum;
}
int operator-(Goods &obj1, Goods &obj2)
{
int sub = obj1.money - obj2.money;
cout << "价格差值为" << sub << "元" << endl;
return sub;
}
void test1()
{
Goods phone("iphone 13", 5999, "2021/9/16");
Goods computer("Lenovo", 6888, "2020/12/12");
sumMoney(phone, computer);
//只要实现了运算符+和-的重载函数,那么以下表达式执行的时候会自动
//调用对应的运算符重载函数
int sum = phone + computer;
int sub = phone - computer;
cout << sum << ", " << sub << endl;
//运算符重载函数也可以主动调用,但是一般不会这样操作
//operator+(phone, computer);
}
//++运算符的重载函数
//后缀++的运算符重载函数,参数里面必须有一个int参数
int operator++(Goods &obj, int)
{
//先使用后自增
int n = obj.money;
obj.money++;
return n;
}
//前缀++的运算符重载函数
int operator++(Goods &obj)
{
//先自增后使用
obj.money++;
int n = obj.money;
return n;
}
void test2()
{
Goods phone("iphone 13", 5999, "2021/9/16");
Goods computer("Lenovo", 6888, "2020/12/12");
int m1 = phone++;
cout << m1 << ", " << phone.money << endl;
int m2 = ++computer;
cout << m2 << ", " << computer.money << endl;
}
int main()
{
test2();
return 0;
}
2.4 当类的成员是私有时编写运算符重载函数
- 方法1:使用友元函数实现运算符重载
- 方法2:类内定义公有的成员函数实现运算符重载
2.4.1 使用友元函数实现运算符重载
#include <iostream>
using namespace std;
class Goods{
friend int operator+(Goods &obj1, Goods &obj2);
friend int operator-(Goods &obj1, Goods &obj2);
friend int operator++(Goods &obj, int);
friend int operator++(Goods &obj);
public:
Goods(){};
Goods(string name, int money, string date)
{
this->name = name;
this->money = money;
this->date = date;
}
int getMoney()
{
return this->money;
}
private:
string name;
int money;
string date;
};
//+ -运算符的重载
int operator+(Goods &obj1, Goods &obj2)
{
int sum = obj1.money + obj2.money;
cout << "一共话费" << sum << "元" << endl;
return sum;
}
int operator-(Goods &obj1, Goods &obj2)
{
int sub = obj1.money - obj2.money;
cout << "价格差值为" << sub << "元" << endl;
return sub;
}
void test1()
{
Goods phone("iphone 13", 5999, "2021/9/16");
Goods computer("Lenovo", 6888, "2020/12/12");
int sum = phone + computer;
int sub = phone - computer;
cout << sum << ", " << sub << endl;
}
//++运算符的重载函数
int operator++(Goods &obj, int)
{
int n = obj.money;
obj.money++;
return n;
}
//前缀++的运算符重载函数
int operator++(Goods &obj)
{
obj.money++;
int n = obj.money;
return n;
}
void test2()
{
Goods phone("iphone 13", 5999, "2021/9/16");
Goods computer("Lenovo", 6888, "2020/12/12");
int m1 = phone++;
cout << m1 << ", " << phone.getMoney() << endl;
int m2 = ++computer;
cout << m2 << ", " << computer.getMoney() << endl;
}
int main()
{
test1();
return 0;
}
2.4.2 类内定义公有的成员函数实现运算符重载
#include <iostream>
using namespace std;
class Goods{
public:
Goods(){};
Goods(string name, int money, string date)
{
this->name = name;
this->money = money;
this->date = date;
}
//如果将成员函数实现运算符重载,一定注意是调用谁的运算符重载函数,
//因为此时是成员函数,这个对象的成员变量是可以任意操作的
int operator+(Goods &obj)
{
int sum = this->money + obj.money;
return sum;
}
//后缀++的重载函数
int operator++(int)
{
int n = this->money;
this->money++;
return n;
}
//前缀++的重载函数
int operator++()
{
this->money++;
int n = this->money;
return n;
}
private:
string name;
int money;
string date;
};
void test1()
{
Goods phone("iphone 13", 5999, "2021/9/16");
Goods computer("Lenovo", 6888, "2020/12/12");
int sum = phone + computer;
cout << sum << endl;
int add = phone++;
int sub = ++computer;
cout << add << ", " << sub;
}
int main()
{
test1();
return 0;
}
2.4.3 等号=的运算符重载函数
#include <iostream>
#include <string.h>
using namespace std;
//当成员变量中有指针时,构造、析构和拷贝构造的书写
class Person{
public:
//构造函数
Person()
{
cout << "无参构造函数" << endl;
p_id = 0;
p_name = NULL;
p_score = 0;
p_address = NULL;
}
Person(int id, char *name, int score, char *address)
{
cout << "有参构造函数" << endl;
p_id = id;
p_name = new char[strlen(name) + 1];
strcpy(p_name, name);
p_score = score;
p_address = new char[strlen(address) + 1];
strcpy(p_address, address);
}
//析构函数
//释放堆区的空间
~Person()
{
cout << "析构函数" << endl;
if(p_name != NULL)
{
delete []p_name;
p_name = NULL;
}
if(p_address != NULL)
{
delete []p_address;
p_address = NULL;
}
}
//拷贝构造函数
//拷贝构造函数里面需要对指针变量重新开辟空间,防止浅拷贝
Person(const Person &obj)
{
cout << "拷贝构造函数" << endl;
p_id = obj.p_id;
p_name = new char[strlen(obj.p_name) + 1];
strcpy(p_name, obj.p_name);
p_score = obj.p_score;
p_address = new char[strlen(obj.p_address) + 1];
strcpy(p_address, obj.p_address);
}
void printMsg()
{
cout << p_id << ", ";
cout << p_name << ", ";
cout << p_score << ", ";
cout << p_address << endl;
}
void operator=(Person &obj)
{
this->p_id = obj.p_id;
if(this->p_name == NULL)
{
this->p_name = new char[strlen(obj.p_name) + 1];
}
else
{
delete []this->p_name;
this->p_name = new char[strlen(obj.p_name) + 1];
}
strcpy(this->p_name, obj.p_name);
this->p_score = obj.p_score;
if(this->p_address == NULL)
{
this->p_address = new char[strlen(obj.p_address) + 1];
}
else
{
delete []this->p_address;
this->p_address = new char[strlen(obj.p_address) + 1];
}
strcpy(this->p_address, obj.p_address);
}
private:
int p_id;
char *p_name;
int p_score;
char *p_address;
};
void test1()
{
Person p1(1001, "张三", 90, "北京");
p1.printMsg();
Person p2 = p1;
p2.printMsg();
Person p3(1002, "李四", 100, "北京");
p3.printMsg();
//当对象定义完毕之后,同另一个对象给当前对象赋值,
//会调用默认的运算符=的重载函数,这个函数内部默认就是值传递
//但是现在成员变量中有指针变量,如果值传递,对象释放空间调用
//析构函数就会将同一块空间释放两次,出现浅拷贝的错误,
//所以需要重写运算符=的重载函数
p3 = p1;
p3.printMsg();
}
int main()
{
test1();
return 0;
}
2.4.3 练习:自己实现字符串类
mystring.h
#ifndef MYSTRING_H
#define MYSTRING_H
#include <iostream>
#include <cstring>
using namespace std;
class MyString
{
public:
MyString();
MyString(const char *str);
MyString(const MyString &obj);
//[]重载
char &operator[](int index);
//=重载
MyString& operator=(const char * str);
MyString& operator=(const MyString& str);
//字符串拼接 重载+号
MyString operator+(const char * str );
MyString operator+(const MyString& str);
//字符串比较
bool operator== (const char * str);
bool operator== (const MyString& str);
void StringShow();
private:
char *pString;
int m_size;
};
#endif // MYSTRING_H
mystring.cpp
#include "mystring.h"
MyString::MyString()
{
this->pString = NULL;
this->m_size = 0;
}
MyString::MyString(const char *str)
{
this->pString = new char[strlen(str) + 1];
strcpy(this->pString, str);
this->m_size = strlen(str);
}
MyString::MyString(const MyString &obj)
{
this->pString = new char[strlen(obj.pString) + 1];
strcpy(this->pString, obj.pString);
this->m_size = obj.m_size;
}
char &MyString::operator[](int index)
{
static char ch = -1;
if(index >= 0 && index < this->m_size)
{
return this->pString[index];
}
else
{
printf("越界操作了\n");
return ch;
}
}
MyString &MyString::operator=(const char *str)
{
if(this->pString != NULL)
{
delete []this->pString;
this->pString = new char[strlen(str) + 1];
}
else
{
this->pString = new char[strlen(str) + 1];
}
strcpy(this->pString, str);
this->m_size = strlen(str);
return *this;
}
MyString &MyString::operator=(const MyString &str)
{
if(this->pString != NULL)
{
delete []this->pString;
this->pString = new char[strlen(str.pString) + 1];
}
else
{
this->pString = new char[strlen(str.pString) + 1];
}
strcpy(this->pString, str.pString);
this->m_size = str.m_size;
return *this;
}
MyString MyString::operator+(const char *str)
{
if(this->pString != NULL)
{
char *s = new char[this->m_size + 1];
strcpy(s, this->pString);
delete []this->pString;
this->pString = new char[this->m_size + strlen(str) + 1];
strcpy(this->pString, s);
strcat(this->pString, str);
this->m_size += strlen(str);
delete []s;
}
else
{
this->pString = new char[strlen(str) + 1];
strcpy(this->pString, str);
this->m_size = strlen(str);
}
return *this;
}
MyString MyString::operator+(const MyString &str)
{
if(this->pString != NULL)
{
char *s = new char[this->m_size + 1];
strcpy(s, this->pString);
delete []this->pString;
this->pString = new char[this->m_size + strlen(str.pString) + 1];
strcpy(this->pString, s);
strcat(this->pString, str.pString);
this->m_size += strlen(str.pString);
delete []s;
}
else
{
this->pString = new char[strlen(str.pString) + 1];
strcpy(this->pString, str.pString);
this->m_size = strlen(str.pString);
}
return *this;
}
bool MyString::operator==(const char *str)
{
if(strcmp(this->pString, str) == 0)
{
return true;
}
else
{
return false;
}
}
bool MyString::operator==(const MyString &str)
{
if(strcmp(this->pString, str.pString) == 0)
{
return true;
}
else
{
return false;
}
}
void MyString::StringShow()
{
cout << "长度:" << this->m_size << ", ";
cout << "内容:" << this->pString << endl;
}
main.cpp
#include <iostream>
#include "mystring.h"
using namespace std;
int main()
{
MyString m1("hello world");
m1.StringShow();
MyString m2(m1);
m2.StringShow();
cout << m2[3] << endl;
cout << m2[0] << endl;
cout << m2[30] << endl;
m2 = "nihao beijing";
m2.StringShow();
MyString m3;
m3 = m2 = m1;
m1.StringShow();
m2.StringShow();
m3.StringShow();
cout << "********************" << endl;
MyString m4;
m4 = m4 + "hello world";
m4.StringShow();
MyString m5("hahahaha");
m5.StringShow();
m5 = m5 + "hello world";
m5.StringShow();
MyString m6("88888888");
m6 = m6 + m5;
m6.StringShow();
if(m6 == m5)
{
cout << "m6 = m5" << endl;
}
else
{
cout << "m6 != m5" << endl;
}
return 0;
}
作业1:实现<<的运算符重载函数
#include <iostream>
using namespace std;
class Person{
friend ostream &operator<<(ostream &cout, Person &obj);
public:
Person(){}
Person(int id, string name, char sex, int score):
id(id),name(name),sex(sex),score(score){}
void printMsg()
{
cout << this->id << ", ";
cout << this->name << ", ";
cout << this->sex << ", ";
cout << this->score << endl;
}
private:
int id;
string name;
char sex;
int score;
};
ostream &operator<<(ostream &cout, Person &obj)
{
cout << obj.id << ", ";
cout << obj.name << ", ";
cout << obj.sex << ", ";
cout << obj.score;
return cout;
}
int main()
{
Person p1(1001, "张三", 'M', 90);
Person p2(1002, "里斯", 'W', 87);
cout << p1 << endl << p2 << endl;
}