• C++ 重载运算符(详)


    C++ 重载运算符

    运算符重载实质是函数的重载,所以我们先来了解一下重载函数。

    一、重载函数

    重载函数是函数的一种特殊情况,为方便使用,C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,也就是说用同一个函数名完成不同的功能。这就是重载函数。重载函数常用来实现功能类似而所处理的数据类型不同的问题。

    1.1、例程

    #include <iostream>
    using namespace std;
    class DemoClass{
        public:
        void printNumber(int num);
        void printNumber(float num);
    };
    void DemoClass::printNumber(int num)
    {
        cout << "int: " << num << endl;
    }
    void DemoClass::printNumber(float num){
        cout << "float: " << num << endl;
    }
    
    int main(void)
    {
        DemoClass demoClass;
        demoClass.printNumber(1);
        demoClass.printNumber(1.1f);
        return 0;
    }
    

    输出:

    int: 1
    float: 1.1
    

    1.2、备注

    1. 函数名必须一致
    2. 参数类型必须不同
    3. 如果参数类型相同,参数个数必须不同

    二、重载运算符

    运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。

    又是那句话:运算符重载实质是函数的重载

    2.1.1、 二元运算符重载

    #include <iostream>
    using namespace std;
    class DemoClass
    {
        public:
        /*构造函数*/
        DemoClass(){}
        DemoClass(int data);
    
        /*重载 + 号*/
        DemoClass operator +(const DemoClass &obj);
    
        /*输出data*/
        void printData();
        private:
        int data;
    };
    int main(void)
    {
        DemoClass demo1(1);
        (demo1+demo1).printData();
        return 0;
    }
    DemoClass::DemoClass(int data)
    {
         this->data = data;
    }
    DemoClass DemoClass::operator +(const DemoClass &obj)
    {
        return  DemoClass(obj.data+data);
    }
    void DemoClass::printData()
    {
        cout << this->data << endl;
    }
    

    输出:

    2
    

    2.1.1、 一元运算符重载

    此处一元运算符指的是 `++ -- ? ! & -(负号)等

    2.1.1.1 ++ --

    分为前置和后置方式,需要引入一个无用的参数作为区别。作为前置可以返回引用,作为后置只能返回临时对象(然后再自增或者自减)。

        const X& operator ++ ();//前置
        const X operator ++ (int);//后置
        const X& operator -- ();
        const X operator -- (int);

    自加函数友元方式

    由于后置返回的是一个临时对象,所以需要一个新建的对象存起来,就有必要定义一个拷贝构造函数。

    其他一元运算符基本上没有什么特别的。大家自己体会,如果想不明白请留言。

    2.2、备注

    1. 由于运算符重载本质是函数的重载,所以遵循函数重载的要求。
    2. 不得创造c++标准中没有的运算符。
    3. 不得改变优先级和结合性,以及操作数的个数
    4. 当运算符单独出现时依然是本来的用法,重载运算符只能用来进行类间的操作。
    5. 除了类属关系运算符.、成员指针运算符.*、作用域运算符::sizeof运算符和三目运算符?:此外,C++中的所有运算符都可以重载。(比如:关系运算符<,==,<=,>,>=,!=,逻辑运算符,自增自减运算符,位运算运算符,赋值运算符+=,-=,<<=,^=等但不包括=,内存运算符new,delete,new[],delete[]还有一些特殊的运算符=,[],(),->)

    2.3 特殊运算符重载

    2.3.1 =号运算符

    每当一个类中含有指针成员的时候要特别注意。如果使用已经创建的对象进行初始化时会自动调用拷贝构造函数,如果两个对象都已经初始化好了,要用=符号进行对象间的拷贝就会出问题。什么问题呢?
    上代码:

    #include <cstring>
    #include <iostream>
    using namespace std;
    
    class MyString
    {
    public:
        MyString(const char* str){count = strlen(str);pstr = new char[count+1];strcpy(pstr, str);}
        ~MyString(){delete []pstr;}
        void display(void){cout<<pstr<<endl;}
    private:
        char *pstr;
        int count;
    };
    int main(void)
    {
        MyString s1("Hello");s1.display();
        MyString s2("World");s2.display();
    
        s2 = s1;//glibc detected : double free or corruption
        s2.display();
    
        return 0;
    }
    
    /** valgrind内存泄露工具检测结果
    Invalid free() / delete / delete[] / realloc()
    
    LEAK SUMMARY:
      definitely lost: 6 bytes in 1 blocks
    **/
    解释:使用valgrind内存泄露检测工具检测以后发现,出现了二次释放的问题,这个问题出在代码20行,在这个位置s1指针的值被赋给了s2,但是s2原来申请的堆空间没有被释放,s1和s2指向同一块堆空间,程序结束的时候s1与s2的析构函数被调用,于是就double free了。
    

    如何解决这个问题?答案就是重载=,当对象使用=赋值处理指针的时候释放原有的堆空间,新申请一段等长的堆空间进行拷贝,而不是两者指向同一个空间,不同的堆空间仅仅是内容一样就不会double free。

    //核心代码 重载 '=' 号运算符
        MyString& operator=(MyString& ref)
        {
            if (this != &ref)
            {
                delete []pstr;//删除自己申请的空间
                count = ref.count;
                pstr = new char[count+1];//申请等长的新空间
                strcpy(pstr, ref.pstr);//拷贝内存
            }
            return (*this);
        }

    2.3.2 [] 下标运算符

    []符号原规则是用来当作数组运算符,所以只允许有一个参数,不得没有参数,或者有多个参数,允许用户自定义功能。

    #include <iostream>
    using namespace std;
    
    class MyArrary
    {
    public:
        MyArrary(int a1,int a2,int a3, int a4){arr[0]=a1;arr[1]=a2;arr[2]=a3;arr[3]=a4;}
    
        //operator overload : []
        int& operator[](int num)//返回引用才能进行赋值操作
        {
            if ((num < 0) || (num > 4))
            {
                cout <<"error:";
                num =  0;
            }
            return arr[num];
        }
    
    private:
        int arr[4];
    };
    
    int main(void)
    {
        MyArrary ma(1,2,3,4);
        cout << ma[0]<<endl;   //取值
        ma[0] = 5;             //赋值
        cout << ma[5]<<endl;
        return 0;
    }

    输出:

    TODO
    

    []可以做左值又可以做右值,所以返回为引用,便于赋值。

    2.3.2 () 括号运算符

    括号通常用来访问多维数组,也可以自定义作用。同()一样可以做左值又可以做右值,所以返回引用。没有参数个数限制,允许没有参数。

    #include <iostream>
    using namespace std;
    
    class DArrary
    {
    public:
        DArrary(int num){int i = 0; for (i = 0; i< 9;i++) arr[i/3][i%3] = num++;}
        void display(void){int i = 0;for (i = 0; i< 9;i++) cout<<arr[i/3][i%3]<<" ";cout<<endl;}
    
        //operator overload : () Multiple Parameters
        int& operator()(int a, int b)
        {
            return arr[a][b];
        }
    
        //operator overload : ()   Singal Parameter
        int& operator()(int a)
        {
            return arr[a/3][a%3];
        }
        //operator overload : ()  None Parameter
        int& operator()(void)
        {
            return arr[0][0];
        }
    private:
        int arr[3][3];
    };
    int main(void)
    {
        DArrary arr(1);arr.display();
        cout << arr(0,0) << endl;    //取值
        cout << arr(2,2) << endl;    //取值
        arr(0,0) = 11;               //赋值
        arr(2,2) = 22;               //赋值
        cout << arr(0,0) << endl;
        cout << arr(2,2) << endl;
    
        cout << arr(7) << endl;      //取值
        arr(7) = 33;                 //赋值
        cout << arr(7) << endl;
    
        cout << arr() << endl;
        arr() = 111;
        arr.display();
        return 0;
    }

    注意!特殊符号只能重载为非静态的成员函数,不能是友元函数和普通函数

    三、重载运算符之友元函数

    友元函数形式的重载运算符由于友元的特性,没有this指针,所以只要多一个参数就好了。
    一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。

    例程

        friend A operator+(A&,A&);//友元函数

    自加友元
    返回

        friend const CounterB operator++(CounterB& ref, int dump);//后置自加

    重载运算符请尽可能的同原来的含义相同或相近,符合软件工程可理解性、可维护性的要求

    参考材料:

    百度百科之重载函数
    百度百科之运算符重载
    c++运算符重载总结_Wuyuan’s Blog
    C++运算符重载——重载特殊运算符
    C++运算符重载——重载一元运算符

  • 相关阅读:
    类的加载过程
    ASCII码表
    uboot main_loop函数分析
    串行CPU设计
    __attribute__ ((section(".text")))的测试
    NandFlash
    测试gcc的优化选项
    如何编写一个简单的makefile
    UBOOT的多支持性与可裁剪性
    函数指针的使用
  • 原文地址:https://www.cnblogs.com/pzqu/p/9457625.html
Copyright © 2020-2023  润新知