目录
运算符重载
定义:运算符重载就是对已有的运算符进行重新定义,赋予其另一种功能,以适应不同的数据类型
目的:让语法更加简洁
本质:调用另一种函数(是编译器去调用)
关键字:operator
对象名 operator运算符(对象)
通用:
1.一般运算符都重载了基础数据类型
2.作为类的成员函数,重载运算符只能有一个参数
注:
一元运算符:只有一个操作数,比如,a++,a--
二元运算符:有两个操作数,比如,a+b,a-b,a/b,a*b
注意:
1.除了赋值号(=)外,基类中被重载的操作符都将被派生类继承
2.<< 和 >> 操作符最好通过友元函数进行重载
3.不要重载 && 和 || 操作符,因为无法实现短路规则
4.运算符重载不能改变本来寓意,不能改变基础类型寓意☆☆☆
加,减法运算符
加减法运算符重载差不多,这里只展示加法运算符重载,无论是加法和减法,都是第一个参数减或加第二个参数
两个不同的对象相加建议使用全局函数
两个相同的对象相加那么建议使用成员变量
全局方式(建议)
class Maker
{
public:
Maker(int id, int age)
{
this->id = id;
this->age = age;
}
public:
int id;
int age;
};
//全局方式,需要两个参数 //2.编译器调用这个函数
Maker operator+(Maker &p1,Maker &p2)//3.编译器检查参数是否对应,p1,加号左边的,p2,加号右边的
{
Maker temp(p1.id + p2.id, p1.age + p2.age);
return temp;
}
void test01()
{
Maker m1(1, 20);
Maker m2(2, 22);
//+也叫双目运算符
Maker m3 = m1 + m2; //1.编译器看到两个对象相加,那么编译器机会去找有没有operator函数
Maker m4 = m1 + m2+m3;//是从左到右运算,然后赋值给m4
cout << "id:" << m4.id << " age:" << m4.age << endl;
}
成员函数方式
class Maker
{
public:
Maker(int id, int age)
{
this->id = id;
this->age = age;
}
//写成成员函数,那么只需要一个参数,这个参数是加号的右边
Maker operator+(Maker &m2)
{
Maker temp(this->id + m2.id, this->age + m2.age);
return temp;
}
public:
int id;
int age;
};
void test01()
{
Maker m1(1, 20);
Maker m2(2, 22);
//+也叫双目运算符
Maker m3 = m1 + m2; //1.编译器看到两个对象相加,那么编译器机会去找有没有operator函数
Maker m4 = m1 + m2+m3;//是从左到右运算,然后赋值给m4
cout << "id:" << m4.id << " age:" << m4.age << endl;
}
解释:
1.为什么这里的operator函数返回的不是引用类型?
因为:不能返回局部变量的引用。
2.为什么这里的operator函数的参数是引用类型?
因为:参数是引用类型可以避免拷贝,节省时间和空间。
左移运算符重载
<<运算符重载,一般是用来输出对象数据的。
注意:左移运算符重载不能以成员函数的形式重载,因为:左移运算符的形式是cout<<m,如果以成员函数的形式重载,因为类的成员函数重载只能有一个参数,这样就会形成m<<cout的局面,所以是错误的。
全局函数方式(only)
class Maker
{
friend ostream& operator<<(ostream& out, Maker& m);
public:
Maker(int id,string name)
{
this->id = id;
this->name = name;
}
Maker operator-(Maker& m1)
{
Maker temp(this->id - m1.id,this->name);
return temp;
}
private:
int id;
string name;
};
//1.形参和实参是一个对象
//2.不能改变库类中的代码,所以函数返回要用ostream &
//3.ostream中把拷贝函数私有化了
//4.如果要和endl一起使用,那必须返回ostream的对象
//5.左移运算符通常用来输出数据
ostream& operator<<(ostream &out,Maker &m)
{
cout << m.id << endl;
cout << m.name << endl;
return out;
}
解释:
1.为什么这里的operator函数返回的是引用类型?
因为:返回的不是局部对象
2.为什么这里的operator函数要返回ostream对象,而不能返回void呢?
因为:一般输出是cout<<n<<endl;这样子输出,要与endl一起使用,经查询源代码得知,<<重载运算符的左边是ostream类型的对象,所以为了保证cout<<n<<endl;输出不会出错,operator函数要返回ostream对象
3.为什么这里的operator函数的第一个参数要返回引用类型?
因为:ostream对象是库类中的代码,不能改变库类中的代码,且ostream中把拷贝函数私有化了,即不允许拷贝行为的发生,所以要用引用类型。
右移运算符
>>运算符重载,一般是用来输入对象数据的。
注意:右移运算符重载也不能以成员函数的形式重载,理由如上。
全局函数方式(only)
class Maker
{
friend istream& operator>>(istream& in, Maker& m);
private:
string name;
int age;
public:
Maker(string name, int age)
{
this->age = age;
this->name = name;
}
int getAge()
{
return age;
}
};
istream &operator>>(istream& in, Maker& m)
{
in >> m.age>> m.name;
return in;
}
赋值运算符重载
通常是给对象与对象间数据进行赋值操作,使用赋值运算符重载,需要考虑深浅重载的问题
默认的赋值运算符重载
编译器会提供一个默认的赋值运算符重载,一般就是简单的赋值。
class Maker
{
public:
int id;
int age;
public:
Maker()
{
id = 0;
age = 0;
}
Maker(int id, int age)
{
this->age = age;
this->id = id;
}
};
void test()
{
Maker m1(10, 20);
Maker m2;
m2 = m1;//赋值操作
//默认的赋值运算符重载函数进行了简单的赋值操作
cout << m2.id << " " << m2.age << endl;
}
但如同深浅拷贝一样,直接简单的赋值容易产生错误。
比如:
class Student
{
public:
Student(const char *name)
{
pName = new char[strlen(name)+1];
strcpy_s(pName, strlen(name) + 1, name);
}
~Student()
{
if (pName != NULL)
{
delete[] pName;
pName = NULL;
}
}
//拷贝函数,避免浅拷贝
Student(const Student& stu)
{
pName = new char[strlen(stu.pName) + 1];
strcpy_s(pName, strlen(stu.pName) + 1, stu.pName);
}
void printStudent()
{
cout << "Name:" << pName << endl;
}
public:
char* pName;
};
void test02()
{
Student s1("悟空");
Student s2("小玲");
s1.printStudent();
s2.printStudent();
s1 = s2;//简单赋值,s1.pName=s2.pName;,会导致s1.pName原来所指向的内存泄露了,然后调用析构函数,s2.pname指向的空间被释放了两次
s1.printStudent();
s2.printStudent();
}
如以上代码所示,如果我进行的是简单的赋值,那么会执行s1.pName=s2.pName,这样会直接把原s1存储的0x1的地址给直接覆盖掉,导致0x1内存的泄露,到后面函数结束s1和s2一起执行析构函数,又会对同一片空间释放两次。
成员函数(only)
所以,为了解决默认赋值重载造成的问题,我们需要重写赋值运算符重载函数。
步骤:
1.不清楚this->pName指向的空间是否装得下s中的数据,所以得先释放this指向的空间
2.申请堆区空间
3.拷贝数据
4.返回对象本身(this指针的使用)
class Student
{
public:
Student(const char *name)
{
pName = new char[strlen(name)+1];
strcpy_s(pName, strlen(name) + 1, name);
}
~Student()
{
if (pName != NULL)
{
delete[] pName;
pName = NULL;
}
}
Student(const Student& stu)
{
pName = new char[strlen(stu.pName) + 1];
strcpy_s(pName, strlen(stu.pName) + 1, stu.pName);
}
Student &operator=(const Student& s)
{
//1.不清楚this->pName指向的空间是否装得下s中的数据,所以得先释放this指向的空间
if (pName != NULL)
{
delete[] pName;
pName = NULL;
}
//2.申请堆区空间
pName= new char[strlen(s.pName) + 1];
//3.拷贝数据
strcpy_s(this->pName, strlen(s.pName) + 1, s.pName);
//4.返回对象本身
return *this;
}
void printStudent()
{
cout << "Name:" << pName << endl;
}
public:
char* pName;
};
解释:
1.为什么这里的operator函数要返回引用?
因为:赋值操作是存在s1=s2=s3的,这样的话应该是先s2=s3,重载运算符函数返回的是s2,再s1=s2,重载运算符函数返回的是s1,如果不用引用,那么函数会拷贝一份临时对象然后返回,这样的话返回的就不是原来的s2了,而是这个临时对象了
2.为什么这里的operator函数要使用const常量参数?
因为:常量参数确保了对象不会被修改
关系运算符重载
成员函数和全局函数都可以
class Maker
{
public:
int id;
int age;
public:
Maker()
{
id = 0;
age = 0;
}
Maker(int id, int age)
{
this->age = age;
this->id = id;
}
//关系运算符重载
bool operator==(Maker& m)
{
if (this->id == m.id)
return true;
return false;
}
};
void test()
{
Maker p1(1, 20);
Maker p2;
if (p1 == p2)
{
cout << "真" << endl;
}
else
cout << "假" << endl;
}
前置++和后置++运算符重载
通常用来对对象的数据进行++操作
成员函数重载(建议)
前置++和后置++是一元运算符,写在成员函数里时,不需要参数,但是为了区分是前置++还是后置++,就需要占位参数,有占位参数的是后置加加,没占位参数的是前置加加,这里的占位参数没有意义,只是用来区分的。
class Maker
{
friend ostream& operator<<(ostream& out,const Maker& m);
private:
int a;
public:
Maker(int a)
{
this->a = a;
}
//前置加加,++a
Maker& operator++()
{
++this->a;
return *this;
}
//后置加加,a++
Maker operator++(int)//占位参数,必须是int
{
//先返回,后加加
Maker tmp(*this);//1.*this里面的值a是为2
++this->a;//这个对象的a是为3的
return tmp;
}
};
ostream& operator<<(ostream& out,const Maker& m)
{
cout << m.a << endl;
return out;
}
void test02()
{
Maker m1(1);
cout << m1 << endl;//1
cout << ++m1 << endl;//2
cout << m1++ << endl;//2 这里返回的是tmp的拷贝,也就是临时对象
cout << m1 << endl;//3 这里返回的是++this->a的对象
}
后置++的步骤(要做到先返回,后加加):
1.先申请一个临时变量令其与要加加的对象的值相等,这样就能先保持这个值不变
2.让对象加加
3.返回临时变量
所以可知后置++返回的是临时变量,临时变量具有很多的不确定性(解释1.),所以,如果可以,++的选择前置++要优先于后置++
解释:
临时对象问题
1.为什么这里的ostream& operator<<(ostream& out,const Maker& m)第二个参数要使用const?(临时对象问题)
因为:这里是一个临时对象的问题,在test02()函数中,第四行,当我使用了m1++后,因为是后置++所以后置++重载运算符函数结束后返回的是一个临时对象,而临时对象"一般"不能被修改(C++11引入右值引用&&之后,其实临时对象也可以被修改的),临时对象只能用常量引用绑定,临时变量也只能用常量引用绑定,比如:int a=10;int &b=a++;这里a++后产生的就是一个临时变量。
数组下标重载
就是对 [ ] 进行重载,目的是让对象里的数据可以像数组一样操作
成员函数重载(only)
.h文件
class MyArray
{
public:
MyArray();
~MyArray();
MyArray(const MyArray& arr);
MyArray(int capacity, int val = 0);
MyArray& operator=(const MyArray& m);
int& operator[](int index);
private:
int* pArray;//指向堆区空间,储存数据
int mSize;//元素个数
int mCapacity;//容量
};
.cpp文件
MyArray::MyArray()
{
mSize = 0;
mCapacity = 20;
pArray = new int[mCapacity];
for (int i = 0; i < mCapacity; i++)
pArray[i] = 0;
}
MyArray::~MyArray()
{
if (pArray != NULL)
{
delete[] pArray;
pArray = NULL;
}
}
MyArray::MyArray(const MyArray& arr)
{
mSize = arr.mSize;
mCapacity = arr.mCapacity;
pArray = new int[mCapacity];
for (int i = 0; i < mSize; i++)
{
pArray[i] = arr.pArray[i];
}
}
MyArray::MyArray(int capacity, int val)//申明和定义不能同时有函数的默认参数
{
this->mCapacity = capacity;
this->mSize = capacity;
this->pArray = new int[mCapacity];
for (int i = 0; i < mSize; i++)
pArray[i] = val;
}
MyArray& MyArray::operator=(const MyArray& m)
{
if (this->pArray != NULL)
{
delete[] this->pArray;
this->pArray = NULL;
}
this->pArray = new int[m.mCapacity];
this->mCapacity = m.mCapacity;
this->mSize = m.mSize;
for (int i = 0; i < this->mSize; i++)
{
this->pArray[i] = m.pArray[i];
}
return *this;
}
//引用,能当左右值
int& MyArray::operator[](int index)
{
//赋值的时候++,不赋值的时候不++,即返回的是左值的时候++,右值的时候不加加
if (this->mSize<=index)
{
this->mSize++;
}
return this->pArray[index];
}
.cpp
void test02()
{
MyArray arr;
for (int i = 0; i < 20; i++)
{
arr[i] = i + 10;
}
MyArray arr2;
arr2 = arr;
for (int i = 0; i < 20; i++)
{
cout << arr2[i] << " ";
}
cout << endl;
}
注意:在这个函数里,小心一个BUG,就是当你实例化对象时(test02第1行),自动调用的是无参构造函数,无参构造函数里size=0,而你实例化对象后,当你运用数组下标重载直接进行赋值(test02第4行),而不同步修改size的值时,你的size就一直都会是0,后面当你调用赋值运算符重载时,把arr的数组数据传给arr2时(test02第7行),到赋值运算符重载函数的for循环那里,因为size一直都是0,所以这个for循环不会执行,而导致arr2的数据全部错误。这里解决了这个错误,当你运用数组下标重载,会同步修改size的值,不会产生报错
解释:
1.为什么返回的是int类型?
因为:是数组的引用,即arr[]操作的是什么,就返回什么值,比如这里,arr[]操作的是MyArray类中的int *pArray类型,所以arr[]要返回int类型
2.为什么参数也是int类型?
因为:arr[],[]里的是int类型。