标准C++ -- day05

bigflyny / 2023-08-25 / 原文

一、 特殊的运算符重载函数
*  ->   ()  []   new   delete
  1. [ ] 下标运算符
  • 想让一个类对象当成数组1一样使用,可以考虑重载下标运算符,例如:vector 等

  • class Array
    {
        int* ptr;// 首地址
        int cal; // 容量
    public:
        Array(int cal):cal(cal)
        {
            ptr = new int[cal];
        }
        int& operator[](int i)
        {
            if(0 > i||i >= cal)
                throw("Array Over Cal");    // 抛异常
            return ptr[i];
        }
    };
    int main()
    {
        Array arr(10);
        for(int i = 0;i < 10;i++)
        {
            try{
            arr[i] = i;
            }catch(const char* msg){
                cout << msg << endl;
            }
            cout << arr[i] << endl;
        }
    }
    
  • 可以考虑在下标重载函数中做非法下标的判断,让下标的使用更安全

  1. ()函数运算符
    • 重载此运算符可以让一个类对象当做函数一样使用

    • 注意:( ) \ [ ] 均不能实现为全局运算符函数,只能实现成员函数(C++全局中已经有类似的函数实现,所以不能实现)

    • = 赋值运算符也不能实现为全局函数,因为类内本身一定有一个=赋值运算符成员函数

    • class Func
      {
      
      public:
          Func(int num=0){}
          int operator()(int num)const
          {
              cout << "()" << endl;
          } 
      };
      
      Func func(10); // 调用构造函数
      func(100);     // 输出 ()
      
  2. 解引用* 和访问成员的运算符 ->
  • 重载这两个运算符可以让类对象像指针一样使用,智能指针就是通过重载这两个运算符从而像使用指针一样的类
  1. new  、delete 运算符的重载
    • void* operator new(size_t size)

    • C++语法要求重载new运算符的参数必须为size_t,编译器会帮助计算要申请的字节数并传递,返回值必须为void*,编译器会帮助转换成对应的类型指针返回

    class Test
    {
        int num;
    public:
        Test(int num=0):num(num){}
        void* operator new(size_t size)
        {
            return malloc(size);
        }
    };
    
    • void operator delete(void* ptr)

    • C++语法要求重载delete的参数必须为void,编译器帮助转换成void传递

    • 注意:new和delete的成员函数和全局函数的格式一样

    • 如果只是针对某个类想要重载它的new、delete时,则写为成员函数

    • 如果想要所以类型都执行重载版本,实现为全局函数

    • 为什么要重载new\delete?

      1. 可以在重载函数中记录每次分配、释放内存的地址、代码情况、次数情况等到日志中,从而方便检查是否出现内存泄漏,以及泄露位置
      2. 对于字节少、且频繁申请、释放的对象,可以在重载函数中给它多分配点内存从而减少产生碎片的可能
  • 如果显式地实现析构函数,则会多申请4字节存储要析构的次数

  • 如果不显式实现析构,则不会多申请

二、 重载运算符的限制
  1. 不能重载的运算符
::    // 域限定符
.     // 直接访问成员的运算符
?:    // 三目运算符
sizeof // 计算字节数
typeid // 获取类型信息的运算符
  1. 只能重载为全局函数的运算符
<<  // 输出运算符
>>  // 输入运算符
  1. 只能重载为成员函数的运算符
[]
()
=
->
  1. 运算符重载可以自定义运算符执行过程,但是无法改名运算符的优先级

  2. 运算符的操作数量也不能改变

  3. 不能发明新的运算符

建议:

  • 重载运算符要遵循一致性原则,不要随意改变运算符本身的含义

  • 不要忘记实现运算符重载函数的初衷,为了提高可读性,不要随意炫技