类在某种程度上也可以视为数据类型,为了在使用类和对象时可以实现使用数据类型和变量时的操作,C++中引入运算符重载。运算符也可以视为一种函数,重载即通过关键字operator对已有的运算符重新定义,赋予其新的功能用来操作对象。
一、加号运算符重载
重载加号运算符,使对象之间可以直接相加。
重载方式分为两种,可以通过全局函数或成员方法来实现。一般而言采用成员方法来实现,可以保持类的封装性。
class Add;
//Add operator+(const Add& p1, const Add& p2);
Add operator+(const Add& p2, int val);
class Add
{
//friend Add operator+(const Add& p1, const Add& p2);
friend Add operator+(const Add& p2, int val);
public:
Add()
{
}
Add(int a, int b)
{
this->m_A = a;
this->m_B = b;
}
void ShowAdd()
{
cout << "m_A = " << this->m_A << endl;
cout << "m_B = " << this->m_B << endl << endl << endl;
}
//成员方法实现加号运算符重载
Add operator+(const Add& p)
{
Add temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}
运算符重载可以发生函数重载
//Add operator+(int val)
//{
// Add temp;
// temp.m_A = this->m_A + val;
// temp.m_B = this->m_B + val;
// return temp;
//}
private:
int m_A;
int m_B;
};
全局函数实现加号运算符重载
//Add operator+(const Add& p1, const Add& p2)
//{
// Add temp;
// temp.m_A = p1.m_A + p2.m_A;
// temp.m_B = p1.m_B + p2.m_B;
// return temp;
//}
//运算符重载可以发生函数重载
Add operator+(const Add& p, int val)
{
Add temp;
temp.m_A = p.m_A + val;
temp.m_B = p.m_B + val;
return temp;
}
void test()
{
Add p1(1, 1);
Add p2(2, 2);
//成员方法方式
//代码执行时实际为Add p3 = p2.operaor+(&p2, p1)
Add p3 = p2 + p1;
p3.ShowAdd();
//全局函数方式
//代码执行时实际为Add p4 = operator+(p3, 10)
Add p4 = p3 + 3;
p4.ShowAdd();
}
int main()
{
test();
return 0;
}
二、左移运算符重载
重载左移运算符,配合链式编程思想可以实现直接连续输出对象的操作。
只能通过全局函数方式实现左移运算符的重载。
链式编程思想,使函数或成员方法以引用作为返回值的方式返回形参列表中的某个对象,一般为第一个,即首次调用该函数或成员方法的那个对象。
class Out;
ostream& operator<<(ostream& out, Out& p);
class Out
{
friend ostream& operator<<(ostream& out, Out& p);
public:
Out(int a, int b)
{
this->m_A = a;
this->m_B = b;
}
//成员方法方式无法实现左移运算符重载
//该成员方法定义后,若要输出p的内容,需要执行语句为 p << cout ,本质为 p.operaor<<(&p, cout)
//ostream& operator<<(ostream& _cout)
//{
// _cout << "m_A = " << this->m_A << endl;
// _cout << "m_B = " << this->m_B << endl << endl << endl;
// return _cout
//}
private:
int m_A;
int m_B;
};
//全局函数方式实现左移运算符重载
//cout实际上是类ostream的对象,同一工程文件内只允许创建cout这一个对象,所以必须使用引用
ostream& operator<<(ostream& _cout, Out& p)
{
_cout << "m_A = " << p.m_A << endl;
_cout << "m_B = " << p.m_B << endl << endl << endl;
return _cout;
}
void test()
{
Out p1(1, 2);
Out p2(3, 4);
Out p3(5, 6);
//若采用成员方法方式实现重载,输出p1时则需执行该语句
//p1 << cout;
//代码执行时实际为operator+(cout, p1)
cout << p1;
//链式输出
cout << p1 << p2 << p3 << endl;
}
int main()
{
test();
return 0;
}
三、递增运算符重载
重载递增运算符,配合链式编程思想使对象内的整形数据可以直接连续递增。
只能通过成员方法实现左移运算符的重载。
根据递增的性质,递增运算符重载需要设计两个成员方法才能完整实现其功能。
class Increase;
ostream& operator<<(ostream& _cout, Increase Num);
class Increase
{
friend ostream& operator<<(ostream& _cout, Increase Num);
public:
Increase(int num)
{
this->num = num;
}
//成员方法方式实现递增运算符重载—— + + 前置
//以引用作为返回值的方式返回对象实现链式调用
Increase& operator++()
{
//先 + +
this->num++;
//再返回成员属性中整型数据完成递增后的对象
return *this;
}
//成员方法方式实现递增运算符重载—— + + 后置
//使用函数占位参数,以使递增运算符重载可以实现函数重载
//此处为 + + 后置的递增运算符重载实现函数重载时的固定用法
Increase operator++(int)
{
//先返回成员属性中整型数据未递增前的对象
Increase temp = *this;
//再 + +
this->num++;
return temp;
}
private:
int num;
};
//全局函数方式实现左移运算符重载
ostream& operator<<(ostream& _cout, Increase Num)
{
_cout << " " << Num.num << " ";
return _cout;
}
void test()
{
//****************************************************************
//————————————————————————————————
//调用时区分前置后置的具体原理,即以下所述代码实际执行仅为推断
//————————————————————————————————
//****************************************************************
// + + 前置功能
Increase Num1(1);
//代码执行时实际为Num1.operator++(&Num1)
cout << ++Num1 << endl;
cout << Num1 << endl << endl << endl;
// + + 后置功能
Increase Num2(1);
//代码执行时实际为Num2.operator++(&Num2,int)
cout << Num2++ << endl;
cout << Num2 << endl;
}
int main()
{
test();
return 0;
}
四、赋值运算符重载
重载赋值运算符,配合链式编程思想使对象间可以直接连续赋值。
只能通过成员方法实现赋值运算符的重载。
默认情况下,编译器会给一个类自动定义默认赋值运算符重载,对成员属性进行简单的值拷贝,即浅拷贝。当需要在堆区开辟空间存放对象的成员属性时,若采用默认赋值运算符重载,会造成堆区空间重复释放问题,原理与默认拷贝构造函数类似。因此,用户要自定义赋值运算符重载以实现深拷贝。
赋值运算符重载与拷贝构造函数的区别在于,当需要实现赋值操作时调用赋值运算符函数,可以随时使用。当需要创建新的对象时调用拷贝构造函数,仅在创建新对象时使用。
class Assign
{
public:
Assign(int a)
{
//开辟堆区以存储成员属性
this->m_A = new int(a);
}
void ShowAssign()
{
cout << *this->m_A << endl;
}
//成员方法方式实现赋值运算符重载
//以引用作为返回值的方式返回对象实现链式调用
Assign& operator=(Assign& p)
{
if (this->m_A != NULL)
{
delete this->m_A;
this->m_A = NULL;
}
//默认情况下,编译器定义的赋值运算符重载为该语句,即浅拷贝
//this->m_A = p.m_A;
//用户自定义赋值运算符重载,在拷贝时对存储于堆区空间的成员属性开辟新的堆区空间存储,即深拷贝
this->m_A = new int(*p.m_A);
//返回自身
return *this;
}
//对象销毁时最后执行的为析构函数,所以要将该对象开辟的堆区空间释放
~Assign()
{
if (this->m_A != NULL)
{
delete this->m_A;
this->m_A = NULL;
}
}
private:
int* m_A;
};
void test()
{
Assign p1(1);
Assign p2(2);
Assign p3(3);
//连续赋值功能的实现
//代码实际执行时为p3.operator=(&p3, p2).operator(&p3, p2)
p3 = p2 = p1;
cout << "p1的m_A = ";
p1.ShowAssign();
cout << "p2的m_A = ";
p2.ShowAssign();
cout << "p3的m_A = ";
p3.ShowAssign();
}
int main()
{
test();
return 0;
}
五、关系运算符重载
重载关系运算符,可以实现让两个对象直接进行对比的操作。
一般通过成员方法实现赋值运算符的重载。
class Relation
{
public:
Relation(string name, int age)
{
this->Name = name;
this->Age = age;
};
//成员方法方式实现关系运算符重载,返回值为bool类型
bool operator==(Relation& p)
{
if (this->Age == p.Age)
{
return true;
}
else
{
return false;
}
}
//成员方法方式实现关系运算符重载,返回值为bool类型
bool operator!=(Relation& p)
{
if (this->Age == p.Age)
{
return false;
}
else
{
return true;
}
}
private:
string Name;
int Age;
};
void test()
{
Relation Chou1("Chou1", 15);
Relation Chou2("Chou2", 15);
//调用关系运算符 = =
//代码实际执行时为if (Chou1.operator==(&Chou1, Chou2))
if (Chou1 == Chou2)
{
cout << "Chou1和Chou2年龄相等" << endl << endl;
}
else
{
cout << "Chou1和Chou2年龄不相等" << endl << endl;
}
//调用关系运算符 ! =
//代码实际执行时为if (Chou1.operator!=(&Chou1, Chou2))
if (Chou1 != Chou2)
{
cout << "Chou1和Chou2年龄不相等" << endl << endl;
}
else
{
cout << "Chou1和Chou2年龄相等" << endl << endl;
}
}
int main()
{
test();
return 0;
}
六、函数调用运算符重载
重载函数调用运算符,使对象在使用“()”时非常像函数的调用,因此又称为仿函数。
一般通过成员方法实现函数调用运算符的重载。
仿函数内的定义没有固定写法,编写时十分灵活。
class Bracket1
{
public:
//成员方法实现函数调用运算符,又称为仿函数
void operator()(string stringPrint)
{
cout << stringPrint << endl;
}
};
void test1()
{
Bracket1 StringPrint;
//代码实际执行时为StringPrint.operator()(&StringPrint, " 原神! 启动! ")
StringPrint(" 原神! 启动! ");
}
class Bracket2
{
public:
//成员方法实现函数调用运算符,又称为仿函数
int operator()(int val1, int val2)
{
return val1 + val2;
}
};
void test2()
{
//可以先创建对象,通过该对象调用函数调用运算符
Bracket2 Add;
//代码实际执行时为int ret = Add1()(&Add1, 1, 1)
int ret = Add(1, 1);
cout << " Add ( 1, 1) = " << ret << endl;
//也可以直接通过匿名对象调用函数调用运算符
//此处代码在实际执行时为
//cout << "UnNameAdd(10,10) = " << Bracket2 临时匿名对象.operator()(& 临时匿名对象, 10, 10) << endl;
cout << " UnNameAdd(10,10) = " << Bracket2()(10, 10) << endl;
}
int main()
{
test1();
test2();
return 0;
}