关于C++运算符重载的一些困惑详解

一.背景

在复习《C++基础与提高》时,自己实现运算符重载(i++)时,几次都报错。其实还是自己对运算符重载这一部分内容理解得不够透彻,于是再次看了下书上的内容,理解算是加深了一些,于是提笔记录一下。

环境:win10,QT4.8

二.概述

这部分内容主要关于在重载函数中,函数前要不要加const,何时加const,返回类型要不要加&(引用)修饰,何时加&(引用)的问题,还有临时对象的问题。关于为什么要重载,重载的规则,友元重载、成员重载的区别之类的知识点,这里就不赘述了。

三.内容

以类Complex为例

class Complex
{
public:
    Complex(double x = 0, double y = 0)
        :m_x(x), m_y(y){}

    void dis()
    {
        cout<<"("<<m_x<<", "<<m_y<<")"<<endl;
    }
protected:
    double m_x;
    double m_y;
};

1.以实现单目运算符prefix++和surfix++为例。

先提这个例子,一是因为我在复习这块时遇到了一点问题,二是这个有点特别,涉及到哑元的问题。

prefixe++

1).考虑基本数据类型,以int类型为例,如下的操作都是可以的;

int a = 1;
++a;
++++a;

2).先实现基本的语义,代码如下:

Complex Complex::operator++(void)
{
    m_x++;
    m_y++;
    return *this;
}

3)考虑添加

重载函数返回的是对象自身,并且需要修改对象,我们即可以想到返回的是引用类型。注意,此时引用指向的对象在重载函数调用时就已经存在了。

4)先运行一下,看下是否能编译通过

++c1;

++++c1;

此时重载函数实现的效果,与基本类型效果一致,符合预期,此时就不考虑重载函数前面是否加const修饰了。

#include <iostream>

using namespace std;

class Complex
{
public:
    Complex(double x = 0, double y =0)
        :m_x(x), m_y(y){}

    void dis()
    {
        cout<<"("<<m_x<<", "<<m_y<<")"<<endl;
    }

    Complex & operator++(void);
protected:
    double m_x;
    double m_y;
};

Complex & Complex::operator++(void)
{
    m_x++;
    m_y++;
    return *this;
}

int main()
{
    double a = 1.0;
    cout<<++a<<endl;
    ++++a;
    cout<<a<<endl;

    Complex c1(1.0, 2.0);

    Complex cc = ++c1;
    cc.dis();
    cc = ++++c1;  // cc = (c1.operator++()).operator++();
    cc.dis();

    return 0;
}

结果如下

surfix++

为了区分prefix++和surfix++两个成员函数,须使用哑元进行区分(引入 哑元,增加了入参的方式,在调用时不需要添加任何的参数),其实类似一个占位符。

1).考虑基本数据类型,以int类型为例,可以进行的操作和不可以进行的操作

int b = 1;
b++;  // 支持
b++++;  // 不支持

2).先实现基本的语义,代码如下

Complex operator++(int)
    {
        Complex temp = *this;
        m_x++;
        m_y++;
        return temp;
    }

3)考虑添加

可以观察到,重载函数返回的是一个临时对象。若是串联调用,这个临时对象它又会调用一次此重载函数

c1.operator++(0).operator++(0);

调用完,然后就消失了。

此时切不可在返回类型中添加&。原因如下:

【不要返回局部对象的引用或指针】

函数完成后,它所占用的存储空间也随之被释放掉。因此,函数终止意味着局部变量的引用将指向不再有效的内存区域。同样地,函数终止,局部对象被释放,指针将指向一个不存在的对象。

4)先运行一下,看下是否能编译通过

我们会发现,第34行无法通过编译,但是第42行可以通过编译。

5)重载的运算符是否会导致表达式可以被赋值,应该以基础类型为准,如int a, b, c; (a=b)=c;是可以的,而(a+b)=c;是不允许的。返回类型通过加const加以限定来实现。

为了使自定义类型与基本数据类型一致,我们在返回类型前面加上const。重载函数中代码修改为如下

const Complex operator++(int);

修改之后,我们可以看到,第34行和42行均无法通过编译,符合预期。

2.双目运算符+

1)考虑基本类型,以下操作都是支持的

int a1 = 1, a2 = 2, a3 = 3;
int m;
m = a1+a2;
m = a1+(a2+a3);
m = (a1+a2)+a3;

2)先重载=,成员函数如下

Complex & Complex::operator=(const Complex &another)
{
    this->m_x = another.m_x;
    this->m_y = another.m_y;
    return *this;
}

3)再重载运算符+,如下:

因为并未修改传入的参数,所以参数前加了const

Complex Complex::operator+(const Complex &another)
{
    return Complex(this->m_x + another.m_x, this->m_y + another.m_y);
}

4)返回类型是否需要加const呢?

我们再对比下表达式的赋值情况,第49行,对于基本类型,临时对象被赋值的情况编译无法通过,但是第58行,自定义类型却编译通过了。此时,为了使其编译不过,可通过在返回值类型前加const加以限定。

将代码

Complex Complex::operator+(const Complex &another);

修改为如下:

const Complex Complex::operator+(const Complex &another);

5)此时,发现第49和58行均无法通过编译,同时第55行和第57行也编译不过了。

这个是为啥呢?

再仔细看刚修改的代码和第57行代码。重载函数返回类型加了const后,返回的就是const对象了。第57行代码,c1 + c2 + c3; c1 + c2返回的是const对象,而重载函数是一个非const函数。此时,即会报错。

在const修饰类一节中,有学习过:如果const构成函数重载,const对象只能调用const函数,非const对象优先调用非const函数。

调整,在重载函数后面添加const,如下:

const Complex Complex::operator+(const Complex &another) const;

四.结尾

学无止境,继续前行,

参考材料

《C++基础与提高》 王桂林

《C++ Primer》第5版SB、JL、BE

到此这篇关于C++运算符重载的一些困惑的文章就介绍到这了,更多相关C++运算符重载内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ 中重载和运算符重载加号实现矩阵相加实例代码

     C++ 重载+运算符重载加号 实现矩阵相加 学习C++ 基础知识,这里实现简单的实例,记录下自己学习生活,很简单,大家一起看看吧! 实例代码: #include<iostream> #include<iomanip> using namespace std; class Complex { private: int i,j,n,a[2][3]; public: Complex(); Complex operator+(Complex &c); void display()

  • C++运算符重载的方法详细解析

    运算符重载实质上是函数的重载 重载运算符的函数一般格式如下: 函数类型    operator  运算符名称    (形参表列) {对运算符的重载处理} 例如,想将"+"用于Complex(复数)的加法运算,函数的原型可以是这样的: 复制代码 代码如下: Complex operator + (Complex & c1,Complex &c2); 其中,operator是关键字,时候专门用于定义重载运算符的函数的,运算符名称就是C++提供给用户的预定运算符. 注意:函数

  • C++运算符重载规则详解

    C++允许重载的运算符和不允许重载的运算符 C++中绝大部分的运算符允许重载,具体规定见表 不能重载的运算符只有5个: .  (成员访问运算符) .*  (成员指针访问运算符) ::  (域运算符) sizeof  (长度运算符) ?:  (条件运算符) 前两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符和sizeof 运算符的运算对象是类型而不是变量或一般表达式,不具备重载的特征. C++运算符重载的规则 C++对运算符重载定义了如下几条规则. 1) C++不允许用户自己定义新的

  • C++中不能被重载的运算符介绍

    C/C++ 里大多数运算符都可以在 C++ 中被重载.C 的运算符中只有 . 和 ?:(以及 sizeof,技术上可以看作一个运算符)不可以被重载.C++ 增加了一些自己的运算符,除了 :: 和 .* 外,大多数都可以被重载.

  • c++运算符重载基础知识详解

    实际上,很多C++运算符已经被重载.eg:将*运算符用于地址,将得到存储在这个地址中的值,将他用于2个数字时,得到的将是他们的乘积.C++根据操作数的数目和类型来决定采用哪种操作. C++允许将运算符重载扩展到用户定义的类型.例如,允许使用+将两个对象相加.编译器将根据操作数的数目和类型决定使用加法定义.运算符重载可以使代码看起来更自然.例如,将2个数组相加是一种常见的运算.通常,需要使用下面这样的for循环来实现: 复制代码 代码如下: for (int i = 0; i < 20; i++)

  • C++运算符重载 成员函数与友元函数详解

    复制代码 代码如下: #include<iostream>using namespace std;class A{    int x,y;    public:    A(int xx,int yy):x(xx),y(yy){}    A(){x=0;y=0;}    A operator+(const A&b) //不加const限定,也可以    { return A(x+b.x,y+b.y); }    A operator-()    { return A(-x,-y); } 

  • 解析C++中不能重载为友元函数的四个运算符

    C++规定有四个运算符 =, ->, [], ()不可以是全局域中的重载(即不能重载为友员函数),这是为什么呢?现在先说说赋值运算符"="的重载C++规定赋值运算符"="只能重载为类的非静态成员函数,而不可以重载为类的友元函数.不能重载为类的静态成员应该比较容易理解,因为静态成员函数是属于整个类的,不是属于某个对象的,它只能去操作类静态数据成员.而赋值运算符"="是基于对象操作的.那么为什么赋值运算符不可以重载为类的友元函数?像同样都是双目

  • C++重载运算符的规则详解

    (1)C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载.例如,有人觉得BASIC中用"* *"作为幂运算符很方便,也想在C++中将"* *"定义为幂运算符,用"3* *5"表示35,这是不行的. (2)C++允许重载的运算符C++中绝大部分运算符都是可以被重载的. 不能重载的运算符只有5个: .             (成员访问运算符) .*            (成员指针访问运算符) ::             (域运

  • 详解C++编程中的单目运算符重载与双目运算符重载

    C++单目运算符重载 单目运算符只有一个操作数,如!a,-b,&c,*p,还有最常用的++i和--i等.重载单目运算符的方法与重载双目运算符的方法是类似的.但由于单目运算符只有一个操作数,因此运算符重载函数只有一个参数,如果运算符重载函数作为成员函数,则还可省略此参数. 下面以自增运算符"++"为例,介绍单目运算符的重载. [例] 有一个Time类,包含数据成员minute(分)和sec(秒),模拟秒表,每次走一秒,满60秒进一分钟,此时秒又从0开始算.要求输出分和秒的值. #

  • 详解C++编程中的重载流插入运算符和流提取运算符

    C++的流插入运算符"<<"和流提取运算符">>"是C++在类库中提供的,所有C++编译系统都在类库中提供输入流类istream和输出流类ostream.cin和cout分别是istream类和ostream类的对象.在类库提供的头文件中已经对"<<"和">>"进行了重载,使之作为流插入运算符和流提取运算符,能用来输出和输入C++标准类型的数据.因此,凡是用"cout&

随机推荐