【C++】类与对象(三)—运算符重载|const成员函数|取地址及const取地址操作符重载

在这里插入图片描述

前言
运算符重载,自增自减运算符重载,const成员函数,取地址及const取地址操作符重载


文章目录

  • 一、运算符重载
    • 自增和自减运算符重载
  • 二、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)成为函数的第二个参数。


注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为成员函数重载时,第一个参数为隐藏的this
  • .* :: 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成员函数,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对象调用。这实际上是函数重载


思考这些问题:

  1. const对象可以调用非const成员函数吗?
  2. 非const对象可以调用const成员函数吗?
  3. const成员函数内可以调用其它的非const成员函数吗?
  4. 非const成员函数内可以调用其它的const成员函数吗?

  1. const对象可以调用非const成员函数吗?

    • 不可以。const 对象只能调用 const 成员函数,因为 const 对象的目的是确保对象的状态不被修改。调用非 const 成员函数会导致编译错误。
  2. 非const对象可以调用const成员函数吗?

    • 是的。非 const 对象可以调用 const 成员函数。这是因为 const 成员函数对对象的状态有只读的访问权限,不会修改对象的成员变量。因此,非 const 对象可以安全地调用 const 成员函数。
  3. const成员函数内可以调用其他的非const成员函数吗?

    • 是的。在 const 成员函数内部,可以调用其他非 const 成员函数,只要这些非 const 成员函数不会修改对象的成员变量。这是因为 const 成员函数对对象的状态有限制,但它可以调用其他不会修改状态的函数。
  4. 非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;
}

在这里插入图片描述


在这里插入图片描述
如果你喜欢这篇文章,点赞👍+评论+关注⭐️哦!
欢迎大家提出疑问,以及不同的见解。