40、前置操作符和后置操作符

本文深入探讨C++中前置++与后置++操作符的重载实现,解析其背后的编译优化机制及对程序效率的影响。通过实例代码,对比不同情况下两种操作符的性能差异,特别强调在类类型对象上使用前置++操作符以提升效率。

1、值得思考的问题

  • 下面的代码有没有区别?为什么?
i++;      // i的值作为返回值,i自增1
++i;      // i自增1,i的值作为返回值

在这里插入图片描述
从反汇编我们可以看出,在工程里面,i++++i这两行代码完全没有任何差异。原因在于这两行代码都是单行的代码,编译器就将它进行优化(为了高效),不使用返回值,因此这两行代码完全没有任何差异。

2、意想不到的事实

  • 现代编译器产品会对代码进行优化
  • 优化使得最终的二进制程序更加高效
  • 优化后的二进制程序丢失了C/C++的原生语义
  • 不可能从编译后的二进制程序还原C/C++程序

3、思考

++ 操作符可以重载吗?如何区分前置 ++ 和后置 ++?

4、++ 操作符的重载

  • ++操作符可以被重载
    — 全局函数和成员函数均可进行重载(推荐成员函数重载)
    — 重载前置 ++ 操作符不需要额外的参数
    — 重载后置 ++ 操作符需要一个 int 类型的占位参数
#include <iostream>
#include <string>
using namespace std;
class Test
{
private:
	int i;
public:
	Test(int i)
	{
		this->i = i;
	}
	Test& operator ++ ()
	{
		++i;
		return *this;
	}
	Test operator ++ (int )
	{
		Test ret(i);
		i++;
		return ret;
	}
	int getI()
	{
		return i;
	}
};
int main()
{
	Test t(0);
	cout << t.getI() << endl;
	Test tt = ++t;
	cout << t.getI() << endl;
	cout << tt.getI() << endl;
	cout << endl;
	Test t1(0);
	cout << t1.getI() << endl;
	Test t2 = t1++;
	cout << t1.getI() << endl;
	cout << t2.getI() << endl;
	return 0;
} 

在这里插入图片描述
根据上面的分析:

前置++操作符的重载参数类型为空,返回值类型为类引用,原生语义是直接自增1,所以返回值类型必须要是类引用。

后置++操作符的重载参数需要一个占位参数 int,返回值类型为类类型,原生语义是先赋值,再自增1,所以返回值只要是类类型即可。

	Test& operator ++ ()				//前置++操作符的重载
	{
		++i;
		return *this;
	}
	Test operator ++ (int )				//后置++操作符的重载
	{
		Test ret(i);
		i++;
		return ret;
	}

对于后置++操作符的重载,里面函数的内容很巧妙,先自定义一个局部对象 ret ,赋值为 i,i++后,返回原来的对象。

让我们回到最初的起点:

  • 下面的代码有没有区别?为什么?
#include <iostream>
#include <string>
using namespace std;
class Test
{
private:
	int i;
public:
	Test(int i)
	{
		this->i = i;
	}
	Test& operator ++ ()
	{
		++i;
		return *this;
	}
	Test operator ++ (int )
	{
		Test ret(i);				//我只是用 i 来构造 ret
		i++;						//这个 i 和上面的 i 是没有关系的
		return ret;

	}
	int getI()
	{
		return i;
	}
};
int main()
{
	Test t(0);
	t++;
	++t;
	return 0;
}

第33行代码和第34行代码有差异吗?
我觉得有差异,因为他们调用的是完全不同的重载函数。效率上面也有差异,前置的++里面没有生成额外的对象,意味着不需要调用构造函数,也不需要调用析构函数,
而后置的++里面生成了一个局部对象,意味着需要调用构造函数,也需要调用析构函数。
所以这两条语句的效率有差别,和我们刚开始在C++代码看到的汇编代码而得出没有任何差异形成反差。

5、值得思考的问题

  • 下面的代码有没有区别?为什么?这次我们应该如何回答?
i++;      // i的值作为返回值,i自增1
++i;      // i自增1,i的值作为返回值

真正的区别:

  • 对于基础类型的变量
    — 前置++的效率和后置++的效率基本相同
    — 根据项目组编码规范进行选择
  • 对于类类型的对象
    — 前置++的效率高于后置++
    — 尽量使用前置++操作符提高程序效率

—— 完善复数类:
Complex.h

#ifndef _COMPLEX_H_
#define _COMPLEX_H_
#pragma once
class Complex
{
private:
	double a;
	double b;
public:
	Complex(double a = 0, double b = 0);
	double getA();
	double getB();
	double getModulus();

	Complex operator + (const Complex& p);
	Complex operator - (const Complex& p);
	Complex operator * (const Complex& p);
	Complex operator / (const Complex& p);

	bool operator == (const Complex& p);
	bool operator != (const Complex& p);

	Complex& operator = (const Complex& p);

	Complex& operator ++ ();
	Complex  operator ++ (int);
};
#endif

Complex.cpp

#include "Complex.h"
#include <math.h>
Complex::Complex(double a, double b)
{
	this->a = a;
	this->b = b;
}
double Complex::getA()
{
	return a;
}
double Complex::getB()
{
	return b;
}
double Complex::getModulus()
{
	return sqrt(a*a + b*b);
}
Complex Complex::operator + (const Complex& p)
{
	double pa = a + p.a;
	double pb = b + p.b;
	Complex ret(pa, pb);
	return ret;
}
Complex Complex::operator - (const Complex& p)
{
	double pa = a - p.a;
	double pb = b - p.b;
	Complex ret(pa, pb);
	return ret;
}
Complex Complex::operator * (const Complex& p)
{
	double pa = a * p.a - b * p.b;
	double pb = a * p.b + b * p.a;
	Complex ret(pa, pb);
	return ret;
}
Complex Complex::operator / (const Complex& p)
{
	double pc = p.a * p.a + p.b * p.b;
	double pa = (a * p.a + b * p.b) / pc;
	double pb = (b * p.a - a * p.b) / pc;
	Complex ret(pa, pb);
	return ret;
}
bool Complex::operator == (const Complex& p)
{
	return (a == p.a) && (b == p.b);
}
bool Complex::operator != (const Complex& p)
{
	return (!(*this == p));
}
Complex& Complex::operator = (const Complex& p)
{
	if (*this != p)
	{
		a = p.a;
		b = p.b;
	}
	return *this;
}
Complex& Complex::operator ++ ()
{
	a = a + 1;
	b = b + 1;
	return *this;
}
Complex  Complex::operator ++ (int)
{
	Complex ret(a, b);
	a = a + 1;		//成员变量
	b = b + 1;
	return ret;
}

main.cpp

#include <iostream>
#include "Complex.h"
using namespace std;
int main()
{
	Complex c0(0, 0);
	Complex c1 = ++c0;
	cout << c0.getA() << endl;
	cout << c0.getB() << endl;
	cout << c1.getA() << endl;
	cout << c1.getB() << endl;
	cout << endl;
	Complex c(0, 0);
	Complex c2 = c++;
	cout << c.getA() << endl;
	cout << c.getA() << endl;
	cout << c2.getB() << endl;
	cout << c2.getB() << endl;
	
	return 0;
}

在这里插入图片描述

小结:

  • 编译优化使得最终的可执行程序更加高效
  • 前置++操作符和后置++操作符都可以被重载
  • ++操作符的重载必须符合原生语义
  • 对于基础类型,前置++与后置++的效率几乎相同
  • 对于类类型,前置++效率高于后置++
#include <iostream>

using namespace std;

class Test
{
private:
   int i;

public:
    Test(int i)
    {
        this->i = i;
    }

    int getI()
    {
        return i;
    }

    Test& operator++()
    {
        this->i++;
        return *this;
    }

    Test operator++(int)
    {
        Test ret(i);
        this->i++;
        return ret;
    }

};

int main()
{
    Test t(2);
    Test t1 = ++t;

    cout << t1.getI() << endl;

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值