类和对象--运算符重载
1 运算符重载
所谓重载,就是重新赋予新的含义。函数重载就是对一个已有的函数赋予新的含义,使之实现新功能,因此,一个函数名就可以用来代表不同功能的函数,也就是”一名多用”。
运算符重载的本质是函数重载。
重载函数的一般格式如下:
函数类型 operator 运算符名称(形参)
{
重载实体;
}
1.1 加号运算符重载
成员函数:T operator+(T &t1)
全局函数:T operator+(T &t1, T &t2)
- 可以利用成员函数实现加号运算符重载
本质:(*this).operator+(m1) (结合代码看)
注意:可以实现 m1 + m2 + m3 - 可以利用全局函数加友元实现加号运算符重载
本质:operator+(m1, m2) (结合代码看)
注意:不能实现 m1 + m2 + m3 - 通过以上两点,所以使用成员函数实现加号运算符重载
#include <iostream>
using namespace std;
class Myint
{
//friend Myint operator+(Myint &m1, Myint &m2);
public:
Myint(){}
Myint(int a)
{
this->m_A = a;
}
#if 1
// 1.利用成员函数实现加号运算符重载
// 本质: (*this).operator+(m1)
// 可以实现 m1 + m2 + m3
Myint operator+(Myint &m1)
{
Myint temp;
temp.m_A = this->m_A + m1.m_A;
return temp;
}
#endif
int m_A;
};
#if 0
// 2.利用全局函数加友元实现加号运算符重载
// 本质: operator+(m1, m2)
// 不能实现 m1 + m2 + m3
Myint operator+(Myint &m1, Myint &m2)
{
Myint temp;
temp.m_A = m1.m_A + m2.m_A;
return temp;
}
#endif
int main()
{
Myint a(10);
Myint b(20);
Myint c(30);
Myint d;
Myint e;
// 如果利用全局函数加友元实现加号运算符重载,不能实现
d = a + b + c;
// 全局函数和成员函数都可以实现
e = a + d;
cout << d.m_A << endl;
cout << e.m_A << endl;
}
1.2 左移运算符重载
全局函数:ostream& operator<<(ostream &cout, T &t1)
成员函数:ostream& operator<<(ostream &cout)
- 如果利用成员函数重载 ,无法实现让cout 在左侧,因此不用成员重载。
本质:m1.operator<<(cout) // 使用:m1 << cout - 利用全局函数实现左移运算符重载
本质:operator<<(cout, m1) // 使用 cout << m1
注意:返回类型为:ostream& ,是引用,否则无法实现链式编程 cout << m1 << endl - 通过以上两点,所以使用全局函数实现加号运算符重载
如果成员变量为私有的,将此函数设为友元。
#include <iostream>
using namespace std;
class Myint
{
friend ostream& operator<<(ostream &cout, const Myint &m1);
public:
Myint(){}
Myint(int a)
{
this->m_A = a;
}
//试图利用成员函数做<<重载
//ostream& operator<<(ostream &cout) // (*this).operator<<(cout) (*this)<<cout
//{}
private:
int m_A;
};
// 利用全局函数和友元实现左移运算符重载
ostream& operator<<(ostream &cout, const Myint &m1)
{
cout << m1.m_A;
return cout;
}
int main()
{
Myint a(20);
cout << a << endl;
}
1.3 前置后置递增运算符重载
前置运算符重载:T& operator++()
后置运算符重载:T operator++(int)
- 前置后置递增运算符重载通过形参中占位参数区分
- 前置++ 效率高于 后置++ 效率 ,因为后置++会调用拷贝构造,创建新的数据。
#include <iostream>
using namespace std;
class MyInt
{
friend ostream& operator<<(ostream &cout, const MyInt &myint);
public:
MyInt(){}
// 有参构造
MyInt(int m)
{
this->m_A = m;
}
// 前置++
MyInt& operator++() // 返回类型为MyInt&,可以++(++MyInt)
{
// 先加1
++this->m_A;
// 后返回
return *this;
}
// 后置++
MyInt operator++(int) // 返回类型为MyInt,不能 (MyInt++)++
{
// 先保存原始数据
MyInt old(*this);
// 再加1
++this->m_A;
// 再返回原始数据
return old;
}
private:
int m_A;
};
// 如果第二个形参不加const修饰会报错
// 此行:cout << m++ << endl;
// 错误:无法将左值‘std::ostream {aka std::basic_ostream<char>}’绑定到‘std::basic_ostream<char>&&’
ostream& operator<<(ostream &cout, const MyInt &myint)
{
cout << myint.m_A;
return cout;
}
int main()
{
MyInt m(10);
cout << m << endl; // 10
// 后置++
cout << m++ << endl; // 10
// 前置++
cout << ++(++m) << endl; // 13
}
1.4 指针运算符重载
// 重载->运算符
T* operator->()
{
return T的指针;
}
// 重载 * 运算符
Person& operator*()
{
return *(T的指针);
}
/*
智能指针
用途: 托管new出来的对象的释放
设计smartPoint智能指针类,内部维护 Person * ,在析构时候释放堆区new出来的person对象
重载 -> * 让 sp智能指针用起来向真正的指针
*/
#include <iostream>
using namespace std;
class Person
{
public:
Person(){}
Person(int age)
{
this->m_Age = age;
}
void showAge()
{
cout << "年龄为: "<< this->m_Age << endl;
}
~Person()
{}
int m_Age;
};
class smartPoint
{
public:
smartPoint(Person *p)
{
this->m_Ptr = p;
}
//重载->运算符
Person * operator->()
{
return this->m_Ptr;
}
//重载 * 运算符
Person& operator*()
{
return *(this->m_Ptr);
}
// 析构函数
~smartPoint()
{
if (this->m_Ptr != NULL)
{
delete this->m_Ptr;
this->m_Ptr = NULL;
}
}
private:
Person *m_Ptr;
};
int main()
{
// 利用智能指针 管理 new 出来的对内存
// sp为栈内存,出了此作用域会释放然后调用smartPoint的析构函数,此析构函数会释放Person堆内存,以此来实现智能指针管理
smartPoint sp(new Person(10)); // Person *p = new Person(10); smartPoint sp(p);
sp->showAge();
(*sp).showAge();
return 0;
}
1.5 赋值运算符重载
T& operator=( const T &t)
注意事项:如果开辟了堆内存,不仅要实现拷贝构造的深拷贝,同时也要实现赋值运算符重载的深拷贝。
#include <iostream>
#include <string.h>
using namespace std;
//编译器 默认给一个类4个函数 默认构造 析构 拷贝构造 (值拷贝) operator= (值拷贝)
class Person
{
public:
Person(char * name, int age)
{
this->m_Name = new char[strlen(name) + 1];
strcpy(this->m_Name, name);
this->m_Age = age;
}
// 重载=运算符
Person& operator=( const Person &p)
{
//先判断原来堆区释放有内容,如果有先释放
if (this->m_Name != NULL)
{
delete [] this->m_Name;
this->m_Name = NULL;
}
this->m_Name = new char[strlen(p.m_Name) + 1];
strcpy(this->m_Name, p.m_Name);
this->m_Age = p.m_Age;
return *this;
}
// 拷贝构造
Person(const Person & p)
{
this->m_Name = new char[strlen(p.m_Name) + 1];
strcpy(this->m_Name, p.m_Name);
this->m_Age = p.m_Age;
}
~Person()
{
if (this->m_Name!=NULL)
{
delete [] this->m_Name;
this->m_Name = NULL;
}
}
char * m_Name;
int m_Age;
};
int main()
{
Person p1("Tom",10);
Person p2("Jerry",19);
p2 = p1; // 调用=运算符重载的函数
Person p3("", 0);
p3 = p2 = p1; // 调用=运算符重载的函数
// Person p4 = p3; // 此处调用拷贝构造,而不是=赋值
cout << "p1姓名: "<< p1.m_Name << " p1年龄: " << p1.m_Age << endl;
cout << "p2姓名: "<< p2.m_Name << " p2年龄: " << p2.m_Age << endl;
cout << "p3姓名: "<< p3.m_Name << " p3年龄: " << p3.m_Age << endl;
}
1.6 []运算符重载
T& operator[](unsigned int index) T 为int、char等等
#include <iostream>
#include <string.h>
using namespace std;
class MyIntArr
{
public:
MyIntArr(){}
MyIntArr(unsigned int size)
{
this->m_pAdd = new int[size];
this->m_Size = size;
}
MyIntArr(const MyIntArr& arr)
{
this->m_pAdd = new int[arr.m_Size];
memcpy(this->m_pAdd, arr.m_pAdd, arr.m_Size);
this->m_Size = arr.m_Size;
}
// []运算符重载 注意:返回引用,才能作为左值,进行赋值操作 arr[1] = 100;
int& operator[](unsigned int index)
{
return this->m_pAdd[index];
}
~MyIntArr()
{
if (m_pAdd != NULL)
{
delete [] m_pAdd;
m_pAdd = NULL;
}
}
int *m_pAdd; // 在堆区开辟的数组指针
size_t m_Size;
};
int main()
{
MyIntArr arr(10);
for(int ii = 0; ii < 10; ++ii)
{
//*(arr.m_pAdd+ii) = ii+1;
arr[ii] = ii+1; // 使用重载的[]运算符做赋值操作
}
/*
for(int ii = 0; ii < 10; ++ii)
{
cout << *(arr.m_pAdd+ii) << endl;
}
cout << "-------------" << endl;
*/
for(int ii = 0; ii < 10; ++ii)
{
cout << arr[ii] << endl; // 使用重载的[]运算符
}
return 0;
}
1.7 关系运算符重载
bool operator 关系运算符 (const T& t)
#include <iostream>
using namespace std;
class Person
{
public:
Person(){}
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
// 重载==关系运算符
bool operator==(const Person& p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
else
{
return false;
}
}
// 重载!=关系运算符
bool operator!=(const Person& p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return false;
}
else
{
return true;
}
}
string m_Name;
int m_Age;
};
int main()
{
Person p1("张三", 18);
Person p2("张三", 18);
Person p3("张三", 20);
if (p1 == p1)
{
cout << "p1 == p2" << endl;
}
else
{
cout << "p1 != p2" << endl;
}
if (p1 == p3)
{
cout << "p1 == p3" << endl;
}
else
{
cout << "p1 != p3" << endl;
}
}
1.8 函数调用运算符重载 [ ()运算符重载–仿函数 ]
重载()运算符
void operator()(string text)
int operator()(int a,int b)
仿函数写法不固定,比较灵活
#include<iostream>
using namespace std;
class MyPrint
{
public:
// 重载()运算符
void operator()(string text)
{
cout << text << endl;
}
};
class MyAdd
{
public:
// 重载()运算符
int operator()(int a,int b)
{
return a + b;
}
};
void MyPrint2(string str)
{
cout << str << endl;
}
int main()
{
MyPrint myPrint;
myPrint("仿函数:hello world"); // 仿函数 本质是一个对象 函数对象
MyPrint2("普通函数:hello world"); //普通函数
MyAdd myAdd;
cout << myAdd(1, 1) << endl;
cout << MyAdd()(1, 2) << endl; // 匿名函数对象 特点:当前行执行完立即释放
return 0;
}
2 运算符重载总结
- =、[]、() 和 -> 操作符只能通过成员函数进行重载
- << 和 >> 操作符只能通过全局函数配合友元函数进行重载
- 不要重载 && 和 || 操作符,因为无法实现短路规则
建议:将<< 和 >>写成全局函数,其他可重载的符号写到成员即可
本文详细介绍了C++中运算符重载的概念,包括加号、左移、递增、指针、赋值、[]、关系和函数调用等运算符的重载方法,以及注意事项和常见应用场景。特别强调了成员函数与全局函数的选择,以及智能指针和赋值运算符的深拷贝技巧。
349

被折叠的 条评论
为什么被折叠?



