c++重载运算符

一、运算符重载基础

C++将运算符重载扩展到自定义的数据类型,它可以让对象操作更美观。

1.1 运算符重载函数的语法

返回值 operator 运算符(参数列表);

注意:运算符重载函数的返回值类型要与运算符本身的含义一致,比如1+2=312int类型,返回值3应该是int类型。

#include <iostream>
using namespace std;

class Complex {
public:
    int real, imag;
    Complex(int r = 0, int i =0) {real = r; imag = i;}
    Complex operator + (Complex const &obj) {
         Complex res;
         res.real = real + obj.real;
         res.imag = imag + obj.imag;
         return res;
    }
};

int main()
{
    Complex c1(10, 5), c2(2, 4);
    Complex c3 = c1 + c2; // An example call to "operator+"
    //上面这个代码,其实就是Complex c3 = c1.operator+(c2);
}

上面这段代码中,我们定义了一个复数类 Complex,并且使用成员函数实现了加号(+)运算符的重载。在 main 函数中,我们创建了两个复数对象 c1c2,并使用加号(+)将它们相加。这里调用了我们定义的 operator+ 函数。

1.2 非成员函数和成员函数的重载运算符

  • 非成员函数重载运算符函数形参个数与运算符的操作数个数相同

    #include <iostream>
    using namespace std;
    
    class Complex {
    public:
        int real, imag;
        Complex(int r = 0, int i =0) {real = r; imag = i;}
    };
    
    Complex operator + (Complex const &obj1, Complex const &obj2) {
         Complex res;
         res.real = obj1.real + obj2.real;
         res.imag = obj1.imag + obj2.imag;
         return res;
    }
    
    int main()
    {
        Complex c1(10, 5), c2(2, 4);
        Complex c3 = c1 + c2; // An example call to "operator+"
        cout << c3.real << " + i" << c3.imag << endl;//12 + i9
    }
    

    如果运算的是private变量,则应该用友元:

    class Complex {
        friend Complex operator + (Complex const &, Complex const &);
    public:
        Complex(int r = 0, int i =0) {real = r; imag = i;}
    private:
        int real, imag;
    
    };
    
    Complex operator + (Complex const &obj1, Complex const &obj2) {
        Complex res;
        res.real = obj1.real + obj2.real;
        res.imag = obj1.imag + obj2.imag;
        return res;
    }
    
    int main()
    {
        Complex c1(10, 5), c2(2, 4);
        Complex c3 = c1 + c2; // An example call to "operator+"
    }
    
  • 成员函数版本的重载运算符函数形参个数比运算符的操作数个数少一个,其中的一个操作数隐式传递了调用对象。

    #include <iostream>
    using namespace std;
    
    class Complex {
    public:
        int real, imag;
        Complex(int r = 0, int i =0) {real = r; imag = i;}
        Complex operator + (Complex const &obj) {
             Complex res;
             res.real = real + obj.real;
             res.imag = imag + obj.imag;
             return res;
        }
    };
    
    int main()
    {
        Complex c1(10, 5), c2(2, 4);
        Complex c3 = c1 + c2; // An example call to "operator+"
        //上面这个代码,其实就是Complex c3 = c1.operator+(c2);
    }
    

注意:

  1. 如果同时重载了非成员函数和成员函数版本,会出现二义性。

  2. 返回自定义数据类型的引用 可以让多个运算符表达式串联起来。(不要返回局部变量的引用)

  3. 重载函数参数列表中的顺序决定了操作数的位置。

    class Num {
    public:
        Num(int digit = 0) : digit_(digit) {}
    
        int digit_;
    };
    
    Num operator+(const Num &obj1, const Num &obj2) {
        Num result;
        result.digit_ = obj1.digit_ + obj2.digit_;
        return result;
    }
    
    Num operator+(const Num &obj1, int a) {
        Num result;
        result.digit_ = obj1.digit_ + a;
        return result;
    }
    
    Num operator+(int a,const Num &obj1) {
        Num result;
        result.digit_ = obj1.digit_ + a;
        return result;
    }
    
    int main() {
        Num n1(3);
        Num n2(4);
        Num n3(5);
        Num n4 = (n1 + n2) + n3;
        //上面这句话本质就是执行了Num& operator + (Num &obj1, Num &obj2)这个函数
        //展开展开解释就是Num n4 = operator+(operator+(n1,n2),n3)
        cout << n4.digit_ << endl;//12
    
        Num n5 = n1 + 5 + 8;
        cout << n5.digit_ << endl;//16
        //上面这句话本质就是执行了Num operator+(const Num &obj1, int a)这个函数
        //展开展开解释就是Num n5 = operator+(operator+(n1,5),8)
    
        Num n6 =  5 + 8 + n1;
        cout << n6.digit_ << endl;//16
        //上面这句话本质就是执行了Num operator+(int a,const Num &obj1)这个函数
        //展开展开解释就是Num n6 = operator+(13,n1)
    }
    
  4. 重载函数的参数列表中至少有一个是用户自定义的类型,防止程序员为内置数据类型重载运算 符。

    #include <iostream>
    using namespace std;
    
    int operator + (int a, int b) {
        return a * b;
    }
    
    int main()
    {
        int a = 3, b = 4;
        cout << a + b << endl;
    }
    

    上面这段代码中,我们试图为内置数据类型 int 重载加号(+)运算符。但是这样做是不允许的,编译器会报错。

  5. 如果运算符重载既可以是成员函数也可以是全局函数,应该优先考虑成员函数,这样更符合运算 符重载的初衷。

    #include <iostream>
    using namespace std;
    
    class Complex {
    public:
        int real, imag;
        Complex(int r = 0, int i =0) {real = r; imag = i;}
        Complex operator + (Complex const &obj) {
             Complex res;
             res.real = real + obj.real;
             res.imag = imag + obj.imag;
             return res;
        }
    };
    
    int main()
    {
        Complex c1(10, 5), c2(2, 4);
        Complex c3 = c1 + c2; // An example call to "operator+"
    }
    

    上面这段代码中,我们定义了一个复数类 Complex,并且使用成员函数实现了加号(+)运算符的重载。在 main 函数中,我们创建了两个复数对象 c1c2,并使用加号(+)将它们相加。这里调用了我们定义的 operator+ 函数。由于加号(+)运算符既可以使用成员函数也可以使用全局函数来重载,所以我们优先考虑使用成员函数。

  6. 重载函数不能违背运算符原来的含义和优先级。

    #include <iostream>
    using namespace std;
    
    class Test
    {
    private:
        int x;
    public:
        Test(int x = 0) { this->x = x; }
        Test operator+(Test const &obj) { return Test(x * obj.x); }
    };
    
    int main()
    {
        Test t1(5), t2(10);
        Test t3 = t1 + t2; // An example call to "operator+"
    }
    

    上面这段代码中,我们定义了一个类 Test,并且使用成员函数实现了加号(+)运算符的重载。但是在重载函数中,我们将两数进行相乘,不准这样子,会引起歧义。

  7. 不能创建新的运算符。

  8. 以下运算符不可重载:

    • .成员运算符
    • .*成员指针运算符
    • ::作用域解析运算符
    • ?:条件运算符
    • typeid一个RTTI运算符
    • sizeof运算符
    • const_cast强制类型转换运算符
    • dynamic_cast强制类型转换运算符
    • reinterpret_cast强制类型转换运算符
    • static_cast强制类型转换运算符
  9. 以下运算符只能通过成员函数进行重载:

    • = 赋值运算符
    • () 函数调用运算符
    • [] 下标运算符
    • -> 通过指针访问类成员的运算符

二、重载关系运算符

重载关系运算符(==、!=、>、>=、<、<=)用于比较两个自定义数据类型的大小。
可以使用非成员函数和成员函数两种版本,建议采用成员函数版本。

class Student{
private:
    string name;
    string id;
public:
    Student(string name, string id):name(name), id(id){}
    bool operator==(const Student& s) const{
        return (id == s.id)&&(name == s.name);
    }
};


int main()
{
    Student stu1("Tom", "123");
    Student stu2("Tom", "123");
    Student stu3("Tom", "1234");
    cout<<(stu1 == stu2)<<endl;//1
    cout<<(stu1 == stu3)<<endl;//0
}

三、重载左移运算符

在C++中,可以通过重载左移运算符(<<)来实现自定义对象的输出。这使得我们可以定义自己的输出格式,方便调试和日志记录。

  • 重载左移运算符时,只能非成员函数版本
  • 为了输出对象的私有成员,我们可以使用友元函数。

以下是一个示例,演示如何重载左移运算符来输出一个名为Student的自定义对象的成员变量:

class Student{
    friend ostream& operator<<(ostream& os, const Student& stu);
private:
    string name;
    string id;
public:
    Student(string name, string id):name(name), id(id){}

};

ostream& operator<<(ostream& os, const Student& stu){
    os << "Name: " << stu.name << endl;
    os << "ID: " << stu.id << endl;
    return os;
}

int main()
{
    Student stu1("Tom", "123");
    cout<<stu1<<endl;
    //Name: Tom
	//ID: 123
}
  • 问题:为什么返回值用ostream&,第一个参数是ostream& os
    1. 重载左移运算符的返回值使用 ostream&,第一个参数是 ostream& os 是因为这样可以实现连续输出。当我们重载左移运算符时,返回 cout 的引用,这样就可以实现 cout<<m<<endl; 这样的连续输出。
    2. 此外,由于 cout 属于输出流 ostream 类型,所以返回值必须为 ostream。加引用是为了确定全局只能有一个 cout 的对象。
  • 扩展:cout 是 ostream 类的一个对象,它用于将数据输出到标准输出流(通常是屏幕)。它是 C++ 标准库中定义的,用于处理控制台 I/O 的一个对象。

四、重载下标运算符

重载下标运算符允许我们在类中使用类似于数组的访问方式来操作对象中的数组。

  • 必须通过成员函数的形式来重载下标运算符[],以便实现对数组的访问和修改。
  1. 如果不使用重载下标运算符,该怎么访问操作对象中的数组呢。

    class Student {
        int score[3];
    public:
        Student(): score{43, 25, 345} {}
    
        int getScore(int index){
            return score[index];
        }
    
    };
    int main()
    {
        Student stu1;
        cout << stu1.getScore(0) << endl;//43
    }
    
  2. 如果想实现函数调用的表达式当做变量来使用,把返回值加上引用。

    class Student {
        int score[3];
    public:
        Student(): score{43, 25, 345} {}
    
        int& getScore(int index){
            return score[index];
        }
    
    };
    int main()
    {
        Student stu1;
        cout << stu1.getScore(0) << endl;
        stu1.getScore(0)=30;
        cout << stu1.getScore(0) << endl;
    }
    

    但是这样写很奇怪,还是重载下标运算符来实现吧。

  3. 使用重载下标运算符实现,有两种实现方式。

    • 重载非const版本的下标运算符:[]不仅可以访问数组元素,还可以修改数组元素。

    • 重载const版本的下标运算符:[]只能访问而不能修改数组元素。

      class Student {
          int score[3];
      public:
          Student(): score{43, 25, 345} {}
      
          // 重载非const版本的下标运算符
          int& operator[](int i){
              return score[i];
          }
          // 重载const版本的下标运算符
          const int& operator[](int i) const{
              return score[i];
          }
      
      
      };
      int main()
      {
          Student stu1;
          cout<<stu1[0]<<endl;// 43
          stu1[0] = 100;
          cout<<stu1[0]<<endl;// 100
      
          const Student stu2;
          cout<<stu2[0]<<endl;// 43
      }
      

    注意:在实际开发中,我们应该同时提供以上两种形式,这样做是为了适应 const 对象,因为通过 const对象只能调用 const 成员函数,如果不提供第二种形式,那么将无法访问 const 对象的任何数组元素。

    在上述的例子中,const 版本和非 const 版本的下标运算符 operator[] 能够同时存在的原因是 const 修饰符是应用在函数自身上,而不是应用在参数上。这使得它们被视为不同的函数,从而满足了函数重载的条件。一个是非 const 成员函数,另一个是 const 成员函数。

五、重载赋值运算符

在C++中,我们可以通过重载赋值运算符(=)来实现自定义的对象赋值操作。默认情况下,编译器提供的赋值运算符执行浅拷贝,但如果类中存在动态分配的资源(堆区内存),我们通常需要自己实现赋值运算符,以确保正确地管理资源。
C++编译器可能会给类添加四个函数:

  • 默认构造函数,空实现。‘
  • 默认析构函数,空实现。
  • 默认拷贝构造函数,对成员变量进行浅拷贝。
  • 默认赋值函数, 对成员变量进行浅拷贝。

对象的赋值运算是用一个已经存在的对象,给另一个已经存在的对象赋值。 如果类的定义中没有重载赋值函数,编译器就会提供一个默认赋值函数。 如果类中重载了赋值函数,编译器将不提供默认赋值函数。

  1. 默认赋值函数(浅拷贝)

    首先,我们来看一个默认的赋值运算符,它执行浅拷贝。假设我们有一个Person类:

    class Student {
    public:
        Student(int age = 0, int tall = 0) : age_(age), tall_(tall) {
    
        }
    
        int age_;
        int tall_;
    
        Student &operator=(const Student &s) {
            //如果不加判断,会出现自己给自己赋值的情况
            if (this != &s){
                age_ = s.age_;
                tall_ = s.tall_;
            }
            return *this;
        }
    };
    
    int main() {
        Student s1(10, 20);
        Student s2(20, 30);
        s2 = s1;
        cout << s2.age_ << endl;//10
        cout << s2.tall_ << endl;//20
    }
    

    在这个例子中,赋值运算符执行了浅拷贝,简单地将成员变量的值从一个对象复制到另一个对象。

  2. 自定义赋值函数(深拷贝)
    现在,我们考虑一个更复杂的情况,其中Student类中包含了一个需要进行深拷贝的指针成员变量。在这种情况下,我们必须自定义赋值运算符,以确保资源的正确管理。

    class Student {
    public:
        Student(int age = 0, int* tall = nullptr) : age_(age), tall_(tall) {
    
        }
    
        int age_;
        int *tall_;
    
        Student &operator=(const Student &s) {
            // 1. 判断是否是自己给自己赋值
            if (this == &s) {
                return *this;//返回自己
            }
            // 2. 释放自己的空间
            if(s.tall_== nullptr){//如果源对象的指针为空
                if(this->tall_!= nullptr){//如果自己的指针不为空
                    delete this->tall_;//释放自己的指针
                    this->tall_ = nullptr;//将自己的指针置空
                }
    
            }//如果源对象的指针不为空
            else{
                if(this->tall_== nullptr){//如果自己的指针为空
                    this->tall_ = new int;//给自己的指针分配空间
                }
                memcpy(this->tall_, s.tall_, sizeof(int));//将源对象的指针的内容拷贝到自己的指针中
            }
            this->age_ = s.age_;//将源对象的非堆区变量拷贝到自己的非堆区变量中
            return *this;
        }
    };
    
    int main() {
        int *a = new int (10);
        int *b = new int (20);
        Student s1(10, a);
        Student s2(20, b);
        s2 = s1;
        cout << s2.age_ << endl;//10
        cout << *s2.tall_ << endl;//10
    }
    

    注意:

    • 编译器提供的默认赋值函数,是浅拷贝。
    • 如果对象中不存在堆区内存空间,默认赋值函数可以满足需求,否则需要深拷贝。
    • 赋值运算和拷贝构造不同:拷贝构造是指原来的对象不存在,用已存在的对象进行构造;赋值 运算是指已经存在了两个对象,把其中一个对象的成员变量的值赋给另一个对象的成员变量。

六、重载new和delete运算符

重载 new 和 delete 运算符可以用来自定义内存的分配和释放过程,例如实现内存池以实现快速分配和归还、避免碎片等。在 C++ 中,使用 new 时,编译器会执行两个主要操作:调用标准库函数 operator new() 分配内存,并调用构造函数初始化对象;使用 delete 时,编译器会执行两个操作:调用析构函数销毁对象,并调用标准库函数 operator delete() 释放内存。我们无法控制构造函数和析构函数的调用,但是可以重载内存分配函数 operator new() 和释放函数 operator delete()。

  1. 重载内存分配函数 operator new()

    void* operator new(size_t size);
    
    • 参数:size_t,表示需要分配的内存大小,size_t就是 unsigned long long
    • 返回值:void*,指向分配的内存块的指针。
  2. 重载内存释放函数 operator delete()

    void operator delete(void* ptr);
    
    • 参数:void*,指向由 operator new() 分配的内存块的指针。
  • 非成员函数版本

    class Student {
        public:
        int age_;
        int score_;
        Student(int age,int score):age_(age),score_(score) {
            cout << "构造函数" << endl;
        }
        ~Student() {
            cout << "析构函数" << endl;
        }
    };
    
    void* operator new(size_t size) {
        void *p = malloc(size);
        cout << "全局重载new:申请了" << size << "字节的地址:" << p << endl;
        return p;
    }
    
    void operator delete(void *p) {
        cout << "全局重载delete:释放了地址:" << p << endl;
        free(p);
    }
    
    int main() {
        int *p = new int(3);
        delete p;
        cout<<endl;
    
        Student *s = new Student(18,100);
        delete s;
    }
    全局重载new:申请了4字节的地址:0x60000307c040
    全局重载delete:释放了地址:0x60000307c040
    
    类中重载new:申请了8字节的地址:0x60000307c040
    构造函数
    析构函数
    类中重载delete:释放了地址:0x60000307c040
    
  • 成员函数版本
    我们也可以在类中重载 operator new 和 operator delete。虽然不必显式使用 static 关键字,但实际上仍然创建了类的静态成员函数

    class Student {
        public:
        int age_;
        int score_;
        Student(int age,int score):age_(age),score_(score) {
            cout << "构造函数" << endl;
        }
        ~Student() {
            cout << "析构函数" << endl;
        }
        void* operator new(size_t size){
            void * p = malloc(size);
            cout << "类中重载new:申请了" << size << "字节的地址:" << p << endl;
            return p;
        }
    
        void operator delete(void *p){
            cout << "类中重载delete:释放了地址:" << p << endl;
            free(p);
        }
    };
    
    
    int main() {
        Student *s = new Student(18,100);
        delete s;
    }
    类中重载new:申请了8字节的地址:0x600000cf0040
    构造函数
    析构函数
    类中重载delete:释放了地址:0x600000cf0040
    
  • 实现简单内存池:

    class Student
    {
    public:
        int id_;
        int age_;
    
        static char *pool;//内存池,使用char好计算
    
        Student(int id,int age):id_(id),age_(age){
        }
        ~Student(){
            cout<<"析构函数"<<endl;
        }
    
        //初始化内存池
        static bool initPool(){
            pool = (char*)malloc(18);//这里设置为18,是因为一个Student占8个字节,第一个字节用于判断后面8个字节是否空闲,然后可以存两个
            if(pool == nullptr){
                return false;
            }
            memset(pool,0,18);//初始化内存池
            cout<<"初始化内存池,首地址:"<<(void*)pool<<endl;
            return true;
        }
    
        //释放内存池
        static void freePool(){
            if(pool != nullptr){//释放内存池
                free(pool);
                pool = nullptr;
                cout<<"释放内存池"<<endl;
            }
    
        }
    
        //重载new
        void* operator new(size_t size){
            if(pool[0]==0){//内存池第一个字节为0,说明后8个字节空闲
                pool[0] = 1;//将第一个字节置为1,表示已经被占用
                cout<<"申请到第一块内存,首地址:"<<(void*)(pool+1)<<endl;
                return pool+1;//返回第二个字节的地址
            }
            else if(pool[9]==0){//内存池第9个字节为0,说明后8个字节空闲
                pool[9] = 1;//将第9个字节置为1,表示已经被占用
                cout<<"申请到第二块内存,首地址:"<<(void*)(pool+10)<<endl;
                return pool+10;//返回第10个字节的地址
            }
            else{
                //内存池满了,就向系统申请内存
                void *p = malloc(size);
                cout<<"申请到系统内存,首地址:"<<(void*)p<<endl;
                return p;
            }
    
        }
    
        //重载delete
        void operator delete(void *p){
            if(p== nullptr){
                return;
            }
            if(p==pool+1){
                pool[0] = 0;//将第一个字节置为0,表示空闲
                cout<<"归还第一块内存,首地址:"<<(void*)(pool+1)<<endl;
            }
            else if(p==pool+10){
                pool[9] = 0;//将第9个字节置为0,表示空闲
                cout<<"归还第二块内存,首地址:"<<(void*)(pool+10)<<endl;
            } else{
                free(p);//如果不属于内存池,就归还给系统
                cout<<"归还系统内存,首地址:"<<(void*)p<<endl;
            }
        }
    
    };
    
    char* Student::pool= nullptr;//静态成员变量初始化
    
    int main()
    {
        Student::initPool();//初始化内存池,首地址:0x6000034651c0
    
        Student *stu1 = new Student(1,18);
        Student *stu2 = new Student(2,19);
        Student *stu3 = new Student(3,20);
    
        delete stu1;
        Student *stu4 = new Student(4,21);
        delete stu2;
        delete stu3;
        delete stu4;
    
        Student::freePool();//释放内存池
    
    }
    

七、重载括号运算符

括号运算符()可以被重载,使得对象可以像函数一样被调用,这种对象被称为函数对象或者仿函数。括号运算符重载函数的语法如下:

返回值类型 operator()(参数列表);
  • 注意:
    1. 括号运算符必须以成员函数的形式进行重载。

    2. 括号运算符重载函数具备普通函数全部的特征。

    3. 如果函数对象与全局函数同名,按作用域规则选择调用的函数。

      •   void show(int a){
              cout<<"全局函数"<<endl;
              cout<<a<<endl;
          }
          
          class Student{
          public:
              void operator()(int a)
              {
                  cout<<"成员函数"<<endl;
                  cout<<a<<endl;
              }
          };
          int main()
          {
              show(3);
              Student s;
              s(4);
          }
          全局函数
          3
          成员函数
          4
        
      •   void show(int a){
              cout<<"全局函数"<<endl;
              cout<<a<<endl;
          }
          
          class Student{
          public:
              void operator()(int a)
              {
                  cout<<"成员函数"<<endl;
                  cout<<a<<endl;
              }
          };
          int main()
          {
          
              Student show;
              show(4);
              ::show(5);//通过增加::,来使用全局函数
          }
          成员函数
          4
          全局函数
          5
        
  • 函数对象的用途(后续再介绍):
    1. 表面像函数,部分场景中可以代替函数,在STL中得到广泛的应用。
    2. 函数对象本质是类,可以用成员变量存放更多的信息。
    3. 函数对象有自己的数据类型。
    4. 可以提供继承体系。

八、重载一元运算符

一元运算符是只操作一个操作数的运算符。C++中,可重载的一元运算符有:

  1. ++ 自增
  2. -- 自减
  3. ! 逻辑非
  4. & 取地址
  5. ~ 二进制反码
  6. * 解引用
  7. + 一元加
  8. - 一元求反

也可分成员函数版本和非成员函数版本。
一元运算符通常出现在它们所操作的对象的左边。但是,自增运算符++和自减运算符–有前置和后置之分。

  • 前置自增和自减:运算符位于操作数的前面,先进行自增或自减操作,然后返回新值。

  • 后置自增和自减:运算符位于操作数的后面,先返回原始值,然后再进行自增或自减操作。

  • C++规定,重载++--时,如果重载函数有一个int形参,编译器处理后置表达式时将调用这个重载函数。

    #include <iostream>
    
    class CGirl {
    private:
        int age;
    
    public:
        CGirl(int a) : age(a) {}
    
        // 前置递增重载
        CGirl& operator++() {
            ++age;
            return *this; // 返回对象的引用
        }
    
        // 后置递增重载
        CGirl operator++(int) {
            CGirl temp(*this); // 保存原始值的副本
            ++age;
            return temp; // 返回临时副本
        }
    
        // getter函数用于获取年龄值
        int getAge() const {
            return age;
        }
    };
    
    int main() {
        CGirl girl(20);
    
        std::cout << "Original age: " << girl.getAge() << std::endl;
    
        CGirl result2 = girl++; // 后置递增,返回临时副本
        std::cout <<result2.getAge() << std::endl;
        std::cout<<girl.getAge()<<std::endl;
    
        CGirl& result1 = ++girl; // 前置递增,返回引用
        std::cout<< result1.getAge() << std::endl;
    
        return 0;
        
        Original age: 20
    	20
    	21
    	22
    }
    

    注意:前置递增(++i)和前置递减(--i)返回的是一个引用,而后置递增(i++)和后置递减(i--)返回的是一个临时副本。

    • 前置递增和递减:
      前置递增(++i):首先进行递增操作,然后返回自身的引用(即CGirl&类型)。
      前置递减(--i):首先进行递减操作,然后返回自身的引用(即CGirl&类型)。

    前置递增和递减返回引用的原因是为了支持连续多次递增或递减操作,以及链式表达式的正确计算(如:(++(++i)))。由于返回引用,可以对同一个对象进行连续操作,而不是每次都产生新的副本。

  • 后置递增和递减:

    • 后置递增(i++):首先返回自身的临时副本(即CGirl类型),然后进行递增操作。
    • 后置递减(i--):首先返回自身的临时副本(即CGirl类型),然后进行递减操作。

    后置递增和递减返回临时副本的原因是,它们需要在递增或递减之前返回对象的原始值,以遵循后缀语义。这样可以确保在表达式中正确计算,而不会影响后续的操作。

  • 逻辑非 ! 运算符重载:

    #include <iostream>
    
    class MyBoolean {
    private:
        bool value;
    
    public:
        MyBoolean(bool val) : value(val) {}
    
        // 逻辑非运算符重载
        bool operator!() const {
            return !value;
        }
    
        // 获取值的方法
        bool getValue() const {
            return value;
        }
    };
    
    int main() {
        MyBoolean b1(true);
        MyBoolean b2(false);
    
        std::cout << "b1 is " << (b1.getValue() ? "true" : "false") << std::endl;
        std::cout << "!b1 is " << (!b1.getValue() ? "true" : "false") << std::endl;
        std::cout << "b2 is " << (b2.getValue() ? "true" : "false") << std::endl;
        std::cout << "!b2 is " << (!b2.getValue() ? "true" : "false") << std::endl;
    
        return 0;
    }
    

    输出:

    b1 is true
    !b1 is false
    b2 is false
    !b2 is true
    
  • 取地址 & 运算符重载:

    #include <iostream>
    
    class MyInt {
    private:
        int value;
    
    public:
        MyInt(int val) : value(val) {}
    
        // 取地址运算符重载
        int* operator&() {
            return &value;
        }
    
        // 获取值的方法
        int getValue() const {
            return value;
        }
    };
    
    int main() {
        MyInt num(42);
        int* ptr = &num; // 调用重载的取地址运算符
    
        std::cout << "num value: " << num.getValue() << std::endl;
        std::cout << "Address of num: " << ptr << std::endl;
        std::cout << "Value at the address: " << *ptr << std::endl;
    
        return 0;
    }
    

    输出:

    num value: 42
    Address of num: 0x7ffe02323a98
    Value at the address: 42
    
  • 二进制反码 ~ 运算符重载:

    #include <iostream>
    
    class MyInteger {
    private:
        int value;
    
    public:
        MyInteger(int val) : value(val) {}
    
        // 二进制反码运算符重载
        MyInteger operator~() const {
            return MyInteger(~value);
        }
    
        // 获取值的方法
        int getValue() const {
            return value;
        }
    };
    
    int main() {
        MyInteger num(42);
        MyInteger complement = ~num; // 调用重载的二进制反码运算符
    
        std::cout << "num value: " << num.getValue() << std::endl;
        std::cout << "Complement value: " << complement.getValue() << std::endl;
    
        return 0;
    }
    

    输出:

    num value: 42
    Complement value: -43
    
  • 解引用 * 运算符重载:

    #include <iostream>
    
    class MyPointer {
    private:
        int* ptr;
    
    public:
        MyPointer(int* p) : ptr(p) {}
    
        // 解引用运算符重载
        int& operator*() const {//返回引用,是为了函数表达式可以当做变量一样来赋值
            return *ptr;
        }
    
        // 获取指针的方法
        int* getPtr() const {
            return ptr;
        }
    };
    
    int main() {
        int value = 42;
        MyPointer ptr(&value); // 创建MyPointer对象并传入指针地址
    
        std::cout << "Original value: " << value << std::endl;
        *ptr = 100; // 调用重载的解引用运算符
        std::cout << "Updated value: " << value << std::endl;
    
        return 0;
    }
    

    输出:

    Original value: 42
    Updated value: 100
    
  • 一元加 + 运算符重载:

    #include <iostream>
    
    class MyNumber {
    private:
        int value;
    
    public:
        MyNumber(int val) : value(val) {}
    
        // 一元加运算符重载
        MyNumber operator+() const {
            return MyNumber(value); // 返回自身的副本
        }
    
        // 获取值的方法
        int getValue() const {
            return value;
        }
    };
    
    int main() {
        MyNumber num(42);
        MyNumber result = +num; // 调用重载的一元加运算符
    
        std::cout << "num value: " << num.getValue() << std::endl;
        std::cout << "Result value: " << result.getValue() << std::endl;
    
        return 0;
    }
    

    输出:

    num value: 42
    Result value: 42
    
  • 一元求反 - 运算符重载:

    #include <iostream>
    
    class MyInteger {
    private:
        int value;
    
    public:
        MyInteger(int val) : value(val) {}
    
        // 一元求反运算符重载
        MyInteger operator-() const {
            return MyInteger(-value);
        }
    
        // 获取值的方法
        int getValue() const {
            return value;
        }
    };
    
    int main() {
        MyInteger num(42);
        MyInteger negation = -num; // 调用重载的一元求反运算符
    
        std::cout << "num value: " << num.getValue() << std::endl;
        std::cout << "Negation value: " << negation.getValue() << std::endl;
    
        return 0;
    }
    

    输出:

    num value: 42
    Negation value: -42
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值