2、 运算符重载(掌握)
2.1 概念
运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
运算符重载的目的:简化操作 让已有的运算符 适应适应不同的数据类型。
C++中可以把部分运算符看做成函数,此时运算符也可以重载。
运算符预定义的操作只能针对基本数据类型,但是对于自定义类型,也需要类似的运算操作,此时就可以重新定义这些运算符的功能,使其支持特定类型,完成特定的操作。
语法:函数的名字由关键字operator及其紧跟的运算符组成
比如:重载+运算符 ==>operator+ 重载=号运算 ==>operator=
注意:重载运算符 不要更改 运算符的本质操作(+是数据的相加 不要重载成相减)
运算符重载有两种实现方式:
● 友元函数运算符重载
● 成员函数运算符重载
2.2 函数运算符重载
2.2.1运算符<<的重载
#include <iostream>
#include<string.h>
using namespace std;
class Person
{
private:
char *name;
int num;
public:
Person(char *name, int num)
{
this‐>name = new char[strlen(name)+1];
strcpy(this‐>name,name);
this‐>num = num;
cout<<"有参构造"<<endl;
}
//普通的成员函数
void printPerson(void)
{
cout<<"name = "<<name<<", num = "<<num<<endl;
}
~Person()
{
if(this‐>name != NULL)
{
delete [] this‐>name;
this‐>name = NULL;
}
cout<<"析构函数"<<endl;
}
};
int main(int argc, char *argv[])
{
Person ob1("lucy",18);
//普通的成员函数 遍历信息
ob1.printPerson();
//cout默认输出方式 无法识别 自定义对象 输出格式
//cout<<ob1<<endl;//err
return 0;
}
解决此问题
解决办法:将operator<<设置成友元:
#include <iostream>
#include<string.h>
using namespace std;
class Person
{
//设置成友元函数 在函数内 访问Person类中的所有数据
friend ostream& operator<<(ostream &out, Person &ob);
private:
char *name;
int num;
public:
Person(char *name, int num)
{
this‐>name = new char[strlen(name)+1];
strcpy(this‐>name,name);
this‐>num = num;
cout<<"有参构造"<<endl;
}
//普通的成员函数
void printPerson(void)
{
cout<<"name = "<<name<<", num = "<<num<<endl;
}
~Person()
{
if(this‐>name != NULL)
{
delete [] this‐>name;
this‐>name = NULL;
}
cout<<"析构函数"<<endl;
}
};
ostream& operator<<(ostream &out, Person &ob)//out=cout, ob =ob1
{
//重新实现 输出格式
out<<ob.name<<", "<<ob.num;
//每次执行为 返回值得到cout
return out;
}
int main(int argc, char *argv[])
{
Person ob1("lucy",18);
//普通的成员函数 遍历信息
//ob1.printPerson();
//cout默认输出方式 无法识别 自定义对象 输出格式
//cout<<ob1<<endl;//err
//运算符重载的调用方式1:
operator<<(cout, ob1)<<endl;
//运算符重载的调用方式2:
//对方法1 进行优化 去掉operator,第一个参数 放在运算符<<的左边 第二个参数 放在运算符<<的右边
cout<<ob1<<endl;//等价operator<<(cout, ob1);
Person ob2("bob",19);
cout<<ob1<<" "<<ob2<<endl;//迭代操作用&
return 0;
}
2.2.2重载+运算符:
全局函数作为友元 完成运算符重载+
#include <iostream>
#include <cstring>
using namespace std;
class Person
{
friend ostream& operator<<(ostream& out, Person& ob); // 友元声明,全局函数作为友元,完成运算符重载 <<
friend Person operator+(Person& ob1, Person& ob2); // 友元声明,全局函数作为友元,完成运算符重载 +
private:
char* name; // 人物名字
int num; // 人物编号
public:
// 无参构造函数
Person()
{
this->name = NULL;
this->num = 0;
cout << "无参构造" << endl;
}
// 有参构造函数
Person(char* name, int num)
{
// 使用 new 关键字动态分配内存,存储人物名字,并进行初始化
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->num = num;
cout << "有参构造" << endl;
}
// 普通成员函数,用于打印对象的数据
void printPerson(void)
{
cout << "name = " << name << ", num = " << num << endl;
}
// 析构函数,用于释放动态分配的内存
~Person()
{
if (this->name != NULL)
{
delete[] this->name;
this->name = NULL;
}
cout << "析构函数" << endl;
}
};
// 友元函数,完成运算符重载 <<
ostream& operator<<(ostream& out, Person& ob)
{
// 重新实现输出格式
out << ob.name << ", " << ob.num;
// 每次执行为返回值得到 cout
return out;
}
// 友元函数,完成运算符重载 +
Person operator+(Person& ob1, Person& ob2)
{
// name + name(字符串追加)
char* tmp_name = new char[strlen(ob1.name) + strlen(ob2.name) + 1];
strcpy(tmp_name, ob1.name);
strcat(tmp_name, ob2.name);
// num + num(数值相加)
int tmp_num = ob1.num + ob2.num;
// 创建临时对象并返回
Person tmp(tmp_name, tmp_num);
// 释放 tmp_name 的空间
if (tmp_name != NULL)
{
delete[] tmp_name;
tmp_name = NULL;
}
return tmp;
}
void test02()
{
Person ob1("lucy", 18);
Person ob2("bob", 19);
cout << ob1 << endl;
cout << ob2 << endl;
// 调用运算符重载 +,实现 ob1 + ob2
Person ob3 = ob1 + ob2;
// 调用运算符重载 <<,实现 cout << ob3
cout << ob3 << endl;
}
int main(int argc, char* argv[])
{
test02();
return 0;
}
1.成员函数 完成运算符重载+
#include <iostream>
#include <cstring> // 包含头文件,用于使用字符串操作函数
using namespace std;
class Person
{
// 友元声明,全局函数作为友元,完成运算符重载 <<
friend ostream &operator<<(ostream &out, Person &ob);
private:
char *name; // 人物名字
int num; // 人物编号
public:
// 无参构造函数
Person()
{
this->name = NULL;
this->num = 0;
cout << "无参构造" << endl;
}
// 有参构造函数
Person(char *name, int num)
{
// 使用 new 关键字动态分配内存,存储人物名字,并进行初始化
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->num = num;
cout << "有参构造" << endl;
}
// 运算符重载 +,ob1 用 this 代替,ob2 用参数 ob 代替
Person operator+(Person &ob)
{
// this ==> &ob1
// name + name(字符串追加)
char *tmp_name = new char[strlen(this->name) + strlen(ob.name) + 1];
strcpy(tmp_name, this->name);
strcat(tmp_name, ob.name);
// num + num(数值相加)
int tmp_num = this->num + ob.num;
// 创建临时对象并返回
Person tmp(tmp_name, tmp_num);
// 释放 tmp_name 的空间
if (tmp_name != NULL)
{
delete[] tmp_name;
tmp_name = NULL;
}
return tmp;
}
// 普通成员函数,用于打印对象的数据
void printPerson(void)
{
cout << "name = " << name << ", num = " << num << endl;
}
// 析构函数,用于释放动态分配的内存
~Person()
{
if (this->name != NULL)
{
delete[] this->name;
this->name = NULL;
}
cout << "析构函数" << endl;
}
};
// 友元函数,完成运算符重载 <<
ostream &operator<<(ostream &out, Person &ob)
{
// 重新实现输出格式
out << ob.name << ", " << ob.num;
// 每次执行为返回值得到 cout
return out;
}
// 测试函数
void test03()
{
// 创建两个 Person 对象
Person ob1("lucy", 18);
Person ob2("bob", 19);
// 调用运算符重载 +,实现 ob1 + ob2
Person ob3 = ob1 + ob2;
Person ob3 = ob1.operator+(ob2)
// 调用运算符重载 <<,实现 cout << ob3
cout << ob3 << endl;
}
// 主函数
int main(int argc, char *argv[])
{
// 调用测试函数
test03();
return 0;
}
2.2.3 重载自增 或自减 ++ --运算符
运算符表达式的函数对应关系如下:
友元函数重载
#include <iostream>
using namespace std;
class MyInt
{
private:
int a;
public:
MyInt(int a):a(a){}
int get_int()
{
return a;
}
// 友元+运算符重载
friend MyInt operator +(MyInt &i1,MyInt &i2);
};
// 友元+运算符重载,类外实现
MyInt operator +(MyInt &i1,MyInt &i2)
{
MyInt int4(0);
int4.a = i1.a + i2.a;
return int4;
}
int main()
{
MyInt int1(2);
MyInt int2(int1); // 拷贝构造函数
MyInt int3 = int1 + int2;
cout << int3.get_int() << endl;
return 0;
}
友元函数运算符重载,实现前置++与后置++
#include <iostream>
using namespace std;
class MyInt
{
private:
int a;
public:
MyInt(int a):a(a){}
int get_int()
{
return a;
}
// 友元+运算符重载
friend MyInt operator +(MyInt &i1,MyInt &i2);
friend MyInt operator ++(MyInt &i); // 前置++
friend MyInt operator ++(MyInt &i,int); // 后置++
};
// 友元+运算符重载,类外实现
MyInt operator +(MyInt &i1,MyInt &i2)
{
MyInt int4(0);
int4.a = i1.a + i2.a;
return int4;
}
MyInt operator ++(MyInt &i)
{
// int - > MyInt 触发构造函数隐式调用
return ++i.a;
}
MyInt operator ++(MyInt &i,int)
{
// int - > MyInt 触发构造函数隐式调用
return i.a++;
}
int main()
{
MyInt int1(1);
MyInt int2(int1); // 拷贝构造函数
MyInt int3 = int1 + int2;
cout << (++int1).get_int() << endl; // 前置++输出2
cout << (int1++).get_int() << endl; // 后置++输出2
cout << int1.get_int() << endl; // 输出3
cout << int3.get_int() << endl;
return 0;
}
2.
2.3.3.1++成员函数运算符重载
成员函数运算符重载相比较于友元函数运算符重载,最主要的区别在于,友元函数的第一个输入参数,在成员函数运算符重载中使用this指针代替。因此通向的运算符重载,成员函数比友元函数参数少一个。
#include <iostream>
using namespace std;
class MyInt
{
private:
int a;
public:
MyInt(int a):a(a){}
int get_int()
{
return a;
}
// 成员函数运算符重载,声明
MyInt operator +(MyInt &i2);
MyInt operator ++(); // 前置++
MyInt operator ++(int); // 后置++
};
// 友元+运算符重载,类外实现
MyInt MyInt::operator +(MyInt &i2)
{
return this->a + i2.a;
}
MyInt MyInt::operator ++()
{
// int - > MyInt 触发构造函数隐式调用
return ++this->a;
}
MyInt MyInt::operator ++(int)
{
// int - > MyInt 触发构造函数隐式调用
return this->a++;
}
int main()
{
MyInt int1(1);
MyInt int2(int1); // 拷贝构造函数
MyInt int3 = int1 + int2;
cout << (++int1).get_int() << endl; // 前置++输出2
cout << (int1++).get_int() << endl; // 后置++输出2
cout << int1.get_int() << endl; // 输出3
cout << int3.get_int() << endl;
return 0;
}
operator++ 编译器看到++a(前置++),它就调用operator++(a),
当编译器看到a++(后置++),它就 会去调用operator++(a,int)
#include <iostream>
#include <string.h>
using namespace std;
class Data
{
friend ostream& operator <<(ostream &out,Data &ob);
private :
int a;
int b;
public:
Data()
{
cout<<"无参构造函数"<<endl;
a=0;
b=0;
}
Data(int a,int b):a(a),b(b)
{
cout<<"有参构造函数"<<endl;
//this->a=a;
//this->b=b
}
void showData(void)
{
cout<<"a= "<< a <<" "<<"b= "<< b <<endl;
}
~Data()
{
cout <<"析构构造函数"<<endl;
}
//成员函数 重载前置++ ++ob1 (先加 后使用)
//编译器 默认识别 operator++(a) //但是a可以用this代替 从而化简 operator++()
Data& operator ++()
{
a++; //this->a = this->a+1
b++;//this->b = this->b+1
return *this;
}
//成员函数 重载后置++ ob1++ (先使用 后加)
//编译器 默认识别 operator++(a,int) //但是a可以用this代替 从而化简 operator++(int)
//static 是全局变量 不加&会重新开辟空间
Data& operator ++(int)
{
//先使用(备份加之前的值)
static Data old = *this;//取内容
//后加
a++;
b++;
//返回备份值
return old;
}
//重载前置‐‐ ‐‐ob3
//编译器 默认识别 operator++(a) //但是a可以用this代替 从而化简 operator‐‐()
Data& operator --()
{
//先减
a--;
b--;
return *this;
}
//重载后‐‐ ob4‐‐
//编译器 默认识别 operator++(a,int) //但是a可以用this代替 从而化简 operator++(int)
Data& operator--(int)
{
//先使用 备份
static Data old = *this;
a--;
b--;
return old;
}
};
//普通全局函数 作为类的友元 重载<<运算符
ostream& operator <<(ostream &out,Data &ob)
{
out<<"a= "<<ob.a<<",b= "<<ob.b;
return out;
}
void test01()
{
Data ob1(10,20);
ob1.showData();
//重载<<直接输出自定义对象的值
cout<<ob1<<endl;//operator<<(cout,ob1);
//成员函数 重载 ++运算符
cout<<++ob1<<endl;
Data ob2(10,20);
cout<<ob2++<<endl;
cout<<ob2<<endl;
//成员函数 重载 ‐‐运算符
Data ob3(10,20);
cout<<"ob3 "<<ob3<<endl;
cout<<--ob3<<endl;
Data ob4(10,20);
cout<<"ob4 "<<ob4<<endl;
cout<<ob4--<<endl;
cout<<"ob4 "<<ob4<<endl;
}
int main()
{
test01();
return 0;
}
2.4 其他运算符重载
部分运算符重载不支持友元函数运算符重载,只支持成员函数运算符重载,比如说(类型转换运算符重载,必须使用成员函数运算符重载)。
2.4.1 赋值运算符重载 =运算符 (重要)
如果程序员不手动编写赋值运算符重载,则赋值运算符重载会被编译器自动添加。
#include <iostream>
using namespace std;
class MyInt
{
private:
int a;
public:
MyInt(int a):a(a){}
int get_int()
{
return a;
}
// 编译器自动添加的赋值运算符重载
MyInt &operator =(MyInt &i)
{
cout << "赋值运算符重载被调用了" << endl;
this->a = i.a;
return *this; // 返回当前对象
}
};
int main()
{
MyInt int1(1);
MyInt int2(int1); // 拷贝构造函数
MyInt int3(2);
int3 = int1; // 赋值运算符重载
MyInt int4 = int3; // 隐式拷贝构造函数
// int4 = int2;
cout << int3.get_int() << endl;
return 0;
}
当类中出现指针类型的成员变量,默认的赋值运算符重载会出现类似于默认的拷贝构造函数,中的“浅拷贝”的问题。因此也需要手动编写解决“浅拷贝”的问题。
2.4.2类型转换运算符重载
必须使用成员函数运算符重载,且格式比较特殊。
#include <iostream>
using namespace std;
class MyInt
{
private:
int a;
string str = "hello";
public:
MyInt(int a):a(a){}
int get_int()
{
return a;
}
// 类型转换运算符重载
operator int()
{
return a;
}
operator string()
{
return str;
}
};
int main()
{
MyInt int1(1);
string a = int1;
cout << a << endl;
return 0;
}
2.4.3 【指针运算符 * ->】(了解)
设计一个智能指针 解决 Person new出的堆区空间 释放问题
#include <iostream>
#include <string.h>
using namespace std;
class Person
{
private :
int num;
public:
Person(int num):num(num)
{
//this‐>num = num;
cout <<"有参函数构造 num="<<num<<endl;
}
void showPerson(void)
{
cout <<"num="<<num<<endl;
}
~Person()
{
cout<<"析构函数 num = "<<num<<endl;
}
};
//设计一个智能指针 解决 Person new出的堆区空间 释放问题
class SmartPointer
{
public:
Person *pPerson;
public:
SmartPointer(Person *p)
{
pPerson = p;
}
~SmartPointer()
{
if(pPerson != NULL)
{
delete pPerson;
pPerson = NULL;
}
}
//成员函数重载‐>运算符
Person* operator->()
{
return this->pPerson;
}
//成员函数 重载 *运算
Person& operator*()
{
return *(this->pPerson);
}
};
void test01()
{
Person *p =new Person(100);
p->showPerson();
//假如我忘了 delete p
delete p;
//需求:自动的帮我释放 堆区空间(智能指针的概念)
SmartPointer pointer(new Person(200));
//访问Person类中的showPerson()
//pointer.pPerson‐>showPerson();
//保证指针的使用
//(pointer.operator ‐>())‐>showPerson();
pointer->showPerson();
(*pointer).showPerson();
}
int main(int argc ,char const*argv[])
{
test01();
return 0;
}
#include <iostream>
#include <string>
using namespace std;
class String {
public:
// 构造函数
String(const string& s) : str(s) {}
// 友元运算符重载
// 实现字符串连接
friend String operator +(const String& s1, const String& s2);
// 实现字符串长度比较
friend bool operator >(const String& s1, const String& s2);
friend bool operator <(const String& s1, const String& s2);
friend bool operator ==(const String& s1, const String& s2);
// 实现字符串不等于比较
bool operator !=(const String& other);
// 赋值运算符重载
String& operator =(const String& s);
// 类型转换运算符,将 String 转换为 string
operator string();
// 成员函数,获取字符串
string get_str();
private:
string str;
};
// 友元运算符重载,实现字符串连接
String operator +(const String& s1, const String& s2)
{
String result = s1.str + s2.str;
return result;
}
// 友元运算符重载,实现字符串长度比较
bool operator >(const String& s1, const String& s2)
{
return s1.str.size() > s2.str.size();
}
bool operator <(const String& s1, const String& s2)
{
return s1.str.size() < s2.str.size();
}
bool operator ==(const String& s1, const String& s2)
{
return s1.str.size() == s2.str.size();
}
// 成员函数运算符重载,实现字符串不等于比较
bool String::operator !=(const String& other)
{
return str.size() != other.str.size();
}
// 赋值运算符重载
String& String::operator =(const String& s)
{
if (this != &s) {
str = s.str;
}
return *this;
}
// 类型转换运算符,将 String 转换为 string
String::operator string()
{
return str;
}
// 成员函数,获取字符串
string String::get_str()
{
return str;
}
int main() {
String s1("Hello");
String s2("World");
// 使用友元运算符重载实现字符串连接
String result = s1 + s2;
cout << "连接结果: " << result.get_str() << endl;
// 使用友元运算符重载实现字符串比较
cout << "比较结果: " << (s1 > s2 ? "s1更大" : "s2 更大") << endl;
// 使用成员函数运算符重载实现字符串不等于比较
cout << "比较结果: " << (s1 != s2 ? "不等于" : "等于") << endl;
// 使用类型转换运算符将 String 转换为 string
string str_result = static_cast<string>(result);
cout << "转换结果: " << str_result << endl;
return 0;
}
2.5 注意事项
● 重载的运算符限制在C++语言中已有的运算符范围,不能创建新的运算符。
● 运算符重载本质上也是函数重载,但是不支持函数默认值设定。
● 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符的操作数和语法结构。
● 运算符重载必须基于或包含自定义类型,即不能改变基本数据类型的运算规则。
● 重载的功能应该与原有功能类似。避免滥用运算符重载。
● 一般情况下,双目运算符,建议使用友元函数运算符重载,单目运算符建议使用成员函数运算符重载。