前言
运算符重载,自增自减运算符重载,const成员函数,取地址及const取地址操作符重载
运算符重载允许重新定义类对象的运算符行为。通过运算符重载,你可以使自定义类型的对象与内置类型一样,使用各种运算符进行操作,从而提高代码的可读性和灵活性。
语法:
//函数名:关键字operator后面接需要重载的运算符符号。
//函数原型:返回值类型 operator操作符(参数列表)
ReturnType operator+(参数) {
// 重载的 + 运算符的实现
// 返回类型可以是任何合适的类型
}
运算符重载可以被重载成类的成员函数,也可以被重载成全局函数。重载成员函数形式的赋值运算符在使用时通过成员访问方式调用,而重载全局函数形式的赋值运算符则直接通过函数名调用。
1. 成员函数形式:
class MyClass {
public:
// 成员函数形式的赋值运算符重载
MyClass& operator=(const MyClass& other) {
// 实现赋值操作
// 返回当前对象的引用
if (this != &other) {
// 实现赋值操作,例如深拷贝资源
}
return *this;
}
};
// 调用
MyClass obj1, obj2;
obj1 = obj2;
在成员函数形式中,运算符重载是通过对象的成员访问方式调用的。左侧的对象(obj1)成为调用对象,右侧的对象(obj2)成为参数传递给成员函数。
2. 全局函数形式:
在全局函数的形式下,函数的参数中不会隐含this指针,因此要传两个参数。
class MyClass {
// MyClass 的声明
};
// 全局函数形式的赋值运算符重载
MyClass& operator=(MyClass& obj1, const MyClass& obj2) {
// 实现赋值操作
// 返回第一个对象的引用
if (&obj1 != &obj2) {
// 实现赋值操作,例如深拷贝资源
}
return obj1;
}
// 调用
MyClass obj1, obj2;
obj1 = obj2;
在全局函数形式中,运算符重载是通过函数名直接调用的。左侧的对象(obj1)成为函数的第一个参数,右侧的对象(obj2)成为函数的第二个参数。
注意:
.*
::
sizeof
?:
.
这5个运算符不能重载。.*
用于访问类成员指针的操作符。前置和后置自增自减运算符的区分是通过参数列表的一个标识符来实现的。具体来说,前置版本没有任何参数,而后置版本有一个额外的(但不使用)int参数。
这里以自增为例:
重载前置自增运算符
ReturnType operator++(){}
重载后置自增运算符
ReturnType operator++(int){}
前置和后置自增的实现:
前置自增运算符重载:
class MyClass {
private:
int value;
public:
// 重载前置自增运算符
MyClass& operator++() {
++value;
return *this;
}
};
后置自增运算符重载:
class MyClass {
private:
int value;
public:
// 重载后置自增运算符
MyClass operator++(int) {
MyClass temp = *this; // 保存原始值
++value; // 执行自增操作
return temp; // 返回原始值
}
};
注意:
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数
隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
以日期类为例:
我们创建一个对象,同时加const修饰,变成常量对象。
class Date {
private:
int _year;
int _month;
int _day;
public:
Date(int year, int month, int day){
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "/" << _month << "/" << _day << endl;
}
};
int main() {
//d1 被声明为常量对象,不可修改
const Date d1(2024, 1, 31);
//报错:“void Date::Print(void)”: 不能将“this”指针从“const Date”转换为“Date &”
//d1.Print();
return 0;
}
当我们调用成员函数Print()
时,编译器会报错,这是因为在通过对象调用成员函数时,this
指针被隐式传参,d1
是只读不可修改,但在Print()
又没有办法对this
指针进行限制,所以会出现权限放大的情况。权限只可以平移,缩小,但是不能放大。
//Date成员函数
void Print() {
cout << _year << "/" << _month << "/" << _day << endl;
}
Date d1(2024, 1, 31);
//d1 是可读可修改,在Print()内同样是可读可修改的,这是权限平移
d1.Print();
//---------------------------------------------------------------------
void Print(const int a) {
cout << _year << "/" << _month << "/" << _day << endl;
}
Date d1(2024, 1, 31);
int a = 5;
//在Print()中a不可被修改,这是权限缩小
d1.Print(a);
所以提出了const成员函数的概念。在函数名后加const
实际上限制了this
指针。
//在函数名后面加上const
void Print() const {
cout << _year << "/" << _month << "/" << _day << endl;
}
//实际上是对this的修饰
void Print(/*const Date* this*/) {
cout << _year << "/" << _month << "/" << _day << endl;
}
另外,如果一个类有两个版本的成员函数,一个是const版本,一个是非const版本,它们可以根据需要被const和非const对象调用。这实际上是函数重载
思考这些问题:
const对象可以调用非const成员函数吗?
非const对象可以调用const成员函数吗?
const成员函数内可以调用其他的非const成员函数吗?
非const成员函数内可以调用其他的const成员函数吗?
Date* operator&(){
return this;
}
const Date* operator&()const{
return this;
}
取地址及const取地址操作符重载也是默认成员函数,不需要显式提供,编译器可以自己生成。
class Date{
private:
int _year;
int _month;
int _day;
public:
Date(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
Date* operator&(){
return this;
}
const Date* operator&()const{
return this;
}
};
int main() {
Date a(2024, 1, 31);
const Date b(2024, 1, 3);
cout << &a << endl;
cout << &b << endl;
return 0;
}