1.运算符重载
在上一篇博客中6个默认的成员函数,其中拷贝赋值中的赋值重载就是运算符重载。
C++为了增强代码的可读性引入运算符重载,运算符重载是具有特殊函数名的函数,其余与普通函数是类似的,也有返回值、形参。
函数名:operator加上需要重载运算符(比如:operator+)
函数:返回值类型 operator加上运算符(参数)
注意:
- 不能通过连接其他符号来创建新的操作符,必须是C++规定的运算符:比如operator@
- 重载操作符必须有一个类类型参数
- 用于内置类型的运算符,其含义最好不要改变,例如:内置的整数-,就是“减”的含义,不要把它写成“加”。
- 作为类成员函数重载时,其形参看起来比操作数数目少一个,因为成员函数第一个参数为隐藏的this
- .* : : sizeof ?: . 以上5个运算符不能重载。
class Date
{
public:
Date(int year = 2024, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//private:
int _year;
int _month;
int _day;
};
//重载为全局,无法访问私有成员,所以以下函数会报错
//解决方法:1、提供成员get、set
// 2、友元(后面才会学习)
// 3、重载成员函数
//
bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}
void Test()
{
Date d1(2024, 1, 3);
Date d2(2024, 2, 3);
//显式调用
operator==(d1, d2);
//直接写,转换调用,编译器会自动转换成operator==(d1, d2);
d1 == d2;
}
类成员重载:
class Date
{
public:
Date(int year = 2024, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// bool operator==(Date* this, const Date& d2)
// 这里需要注意的是,左操作数是this,指向调用函数的对象
bool operator==(const Date& d2)
{
return this->_year == d2._year
&& this->_month == d2._month
&& this->_day == d2._day;//this->可以不加,会自动生成
}
private:
int _year;
int _month;
int _day;
};
1.1赋值运算符重载
赋值运算符重载是一个默认函数,用于完成两个已经存在的对象进行拷贝。
赋值运算符重载是一个运算符重载,规定必须重载为成员函数。
- 参数类型:传递引用可以提高传参效率(减少拷贝构造)
- 返回值类型:返回引用可以提高返回的效率(同样是减少拷贝构造)有返回值的目的是为了支持连续赋值场景。
- 没有显示实现时,会自动生成默认的赋值运算符重载,跟默认构造函数类似,对内置类型完成值拷贝/浅拷贝(一个一个字节拷贝),对于自定义类型会调用他的拷贝构造
实践中总结:
1、如果没有管理资源,编译器自动生成的赋值重载就可以。如Date。
2,、如果都是自定义成员,内置成员也没有资源管理,默认生成的赋值重载就可以。如MyQueue。
3、一般情况下,不需要写析构函数就是不需要写赋值重载。
首先先区分拷贝构造和赋值拷贝(赋值重载)
Date operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date d1(2024, 3, 5);
Date d2 = d1;//这是拷贝构造,1个已经存在的对象,拷贝给另一个要创建的初始化的对象
Date d4(2024, 4, 6);
d1 = d4;//这是赋值拷贝,2个都存在的对象,拷贝赋值给另一个已经存在的对象
上面的例子是单个赋值,如果连续复制呢?
怎么拿到d2,然后再把d2给到d1?
//d1=d2=d4;
Date& operator=(const Date& d)//d是d4的别名
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;//this是d2的指针,*this就代表d2(第一轮赋值)
}
2.取地址运算符重载
2.1const成员函数
- 用const修饰的成员函数叫做const成员函数,const放在函数参数列表后面。比如:bool operator==(const Date& d) const;
首先,先分清楚一个概念:
const在*左边修饰的是指针所指向的内容,代表不能通过指针来改变指针指向的内容。而指针变量本身内容可变。
int main() { int a = 1; int b = 2; const int* p = &a;//int const* p = &a; p = &b;//p指针本身所指内容可以改变 *p = 10;//报错->p指针所指向的内容不能改变 return 0; }
const在*右边修饰的是指针变量本身,代表不能改变指针变量本身的内容。而指针变量所指向的内容可以改变。
int main() { int a = 1; int b = 2; int* const p = &a; p = &b;//报错->p本身内容不可改变 *p = 12;//p所指向的内容可改变 return 0; }
- const实际修饰所指成员函数的this指针,表明在该成员函数中不能对类中任何成员进行修改。
- 改成这样才正确:
2.2取地址运算符重载
取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,一般我们直接用编译器自动实现的就可以了,除非我们不想让别人知道当前类对象的地址,可以自己显式实现,随便返回一个地址。
class Date
{
public :
Date* operator&()
{
return this;
// return nullptr;//返回0000
}
const Date* operator&()const
{
return this;
// return nullptr;
}
Date* operator&()
{
return (Date*)0x3478GH23;//胡乱返回一个地址
}
private :
int _year ;
int _month ;
int _day ;
};
3.类型转换
- C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。
- 构造函数前加explicit就不支持隐式类型转换。
class B
{
public :
//explicit B(int b);不能用隐式构造
B(int b)
{
_b = b;
}
void Print()
{
cout << _b << endl;
}
private:
int _b;
};
int main()
{
//隐式类型转换
B b1 = 2;
b1.Print();
return 0;
}
- 普通类型
- 引用类型
- C++11还支持多参数转化
class B
{
public :
//explicit B(int b);不能用隐式构造
B(int b, int c)
{
_b = b;
_c = c;
}
void Print()
{
cout << _b << _c << endl;
}
private:
int _b;
int _c;
};
int main()
{
//隐式类型转换
B b1 = {3, 4};
b1.Print();
return 0;
}
over~