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);//后置自加


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

相关文章
|
2月前
|
C++
【C++基础】运算符详解
这篇文章详细解释了C++中运算符的用法,包括算术运算符、赋值运算符、比较运算符和逻辑运算符,以及它们在表达式中的作用和示例。
28 2
|
2月前
|
C++
C++(十九)new/delete 重载
本文介绍了C++中`operator new/delete`重载的使用方法,并通过示例代码展示了如何自定义内存分配与释放的行为。重载`new`和`delete`可以实现内存的精细控制,而`new[]`和`delete[]`则用于处理数组的内存管理。不当使用可能导致内存泄漏或错误释放。
|
3月前
|
C++
c++学习笔记02 运算符
C++学习笔记,介绍了C++中的运算符,包括基本的加减乘除、求模、前后置递增递减、赋值运算符、比较运算符和逻辑运算符的使用及其注意事项。
41 6
|
4月前
|
NoSQL 编译器 Redis
c++开发redis module问题之如果Redis加载了多个C++编写的模块,并且它们都重载了operator new,会有什么影响
c++开发redis module问题之如果Redis加载了多个C++编写的模块,并且它们都重载了operator new,会有什么影响
|
4月前
|
存储 C++
【C++】string类的使用③(非成员函数重载Non-member function overloads)
这篇文章探讨了C++中`std::string`的`replace`和`swap`函数以及非成员函数重载。`replace`提供了多种方式替换字符串中的部分内容,包括使用字符串、子串、字符、字符数组和填充字符。`swap`函数用于交换两个`string`对象的内容,成员函数版本效率更高。非成员函数重载包括`operator+`实现字符串连接,关系运算符(如`==`, `&lt;`等)用于比较字符串,以及`swap`非成员函数。此外,还介绍了`getline`函数,用于按指定分隔符从输入流中读取字符串。文章强调了非成员函数在特定情况下的作用,并给出了多个示例代码。
|
4月前
|
NoSQL Redis C++
c++开发redis module问题之避免多个C++模块之间因重载operator new而产生的冲突,如何解决
c++开发redis module问题之避免多个C++模块之间因重载operator new而产生的冲突,如何解决
|
5月前
|
C++
C++之运算符
C++之运算符
|
5月前
|
安全 程序员 C++
C++一分钟之-重载运算符
【6月更文挑战第21天】C++的运算符重载让程序员能为自定义类型定制运算符行为,增强代码表达力。但要注意清晰性、优先级和返回类型。遵循运算符原有意义,充分测试,并用注释解释非直观设计。示例展示了如何为复数类重载`+`运算符。避免重载内置类型,注意结合性,且慎用隐式转换。重载应提升可读性而非复杂化代码。
45 2
|
5月前
|
C++
C++一分钟之-理解C++的运算符与表达式
【6月更文挑战第18C++的运算符和表达式构成了编程的基础,涉及数学计算、逻辑判断、对象操作和内存管理。算术、关系、逻辑、位、赋值运算符各有用途,如`+`、`-`做加减,`==`、`!=`做比较。理解运算符优先级和结合律至关重要。常见错误包括优先级混淆、整数除法截断、逻辑运算符误用和位运算误解。解决策略包括明确优先级、确保浮点数除法、正确使用逻辑运算符和谨慎进行位运算。通过实例代码学习,如 `(a &gt; b) ? &quot;greater&quot; : &quot;not greater&quot;`,能够帮助更好地理解和应用这些概念。掌握这些基础知识是编写高效、清晰C++代码的关键。
37 3