在学习C++过程中我们会发现,对于迭代器和其他模板对象使用前缀形式 (++i) 的自增, 自减运算符,理由是 前置自增 (++i) 通常要比后置自增
(i++) 效率更高。

在《C专家编程》中也讲到:

++a表示取a的地址,增加它的内容,然后把值放在寄存器中;

a++表示取a的地址,把它的值装入寄存器,然后增加内存中的a的值;

例如:对于int类型变量,在进行前置++的时候,是会将i进行加1,然后返回i的引用。而i++是先创建一个i的副本,然后在对i进行加1,最后返回i的副本的值

对于效率不同的问题,让我们从运算符重载的角度来探讨一下!

假设有一个INT类,我们重载了前置++和后置++两个操作符

代码如下:
class INT { private: int value; public: void Print() { cout << " " << value <<
endl; } INT(int v=0) :value(v) { cout << "创建对象 " << this << endl; } ~INT() {
cout << "销毁对象 " << this << endl; } INT(const INT& mc) :value(mc.value) { cout
<< "拷贝构造对象 " << this << endl; } INT& operator++() //前置++ { this->value += 1;
return *this; } const INT operator++(int) //后置++ { INT od = *this; ++*this;
return od; //return INT(this->value++); } operator int const() { return value;
}//重载了一个整型强转 };
从上述代码,我们可以看出前置++和后置++,有3点不同:

* 返回类型不同
* 形参不同
* 效率不同
1.返回值类型的区别

前置++的返回类型是INT&,后置++的返回类型const INT。这意味着,前置++返回的是左值,后置++返回的是右值。 

左值和右值,决定了前置++和后置++的用法
int main() { INT a; (a++)++; //编译错误 ++(a++); //编译错误 a++ = 1; //编译错误 (++a)++;
//OK ++(++a); //OK ++a = 1; //OK }
++的类型是const INT,自然不能对它进行前置++、后置++、赋值等操作。

++a的类型是INT&,当然可以对它进行前置++、后置++、赋值等操作

a++的返回类型为什么要是const对象呢?

有两个原因:

*
如果不是const对象,a(++)++这样的表达式就可以通过编译。但是,其效果却违反了我们的直觉 。a其实只增加了1,因为第二次自增作用在一个临时对象上。
* 另外,对于内置类型,(i++)++这样的表达式是不能通过编译的。自定义类型的操作符重载,应该与内置类型保持行为一致 。
a++的返回类型如果改成非const对象,肯定能通过编译,但是我们最好不要这样做。

++a的返回类型为什么是引用呢?

这样做的原因应该就是:与内置类型的行为保持一致。前置++返回的总是被自增的对象本身。因此,++(++a)的效果就是a被自增两次。

2.形参的区别

前置++没有形参,而后置++有一个int形参,但是该形参也没有被用到。很奇怪,难道有什么特殊的用意?

其实也没有特殊的用意,只是为了绕过语法的限制。

前置++与后置++的操作符重载函数,函数原型必须不同。否则就违反了“重载函数必须拥有不同的函数原型”的语法规定。

虽然前置++与后置++的返回类型不同,但是返回类型不属于函数原型。为了绕过语法限制,只好给后置++增加了一个int形参。

原因就是这么简单,真的没其他特殊用意。其实,给前置++增加形参也可以;增加一个double形参而不是int形参,也可以。只是,当时就这么决定了。

3.效率的区别

如果不需要返回自增之前的值,那么前置++和后置++的计算效果都一样。但是,我们仍然应该优先使用前置++,尤其是对于用户自定义类型的自增操作。

前置++的效率更高,理由是:后置++会生成临时对象。

从INT的后置++的代码实现也可以看出这一点。

const INT operator++(int) //后置++ { INT old = *this; ++*this; //利用前置++ return
old; //return INT(this->value++); //此方法也可以 }
很明显,old是一个临时对象,会造成一次构造函数和一次析构函数的额外开销。虽然,编译器在某些情况下可以优化掉这些开销。但是,我们最好不要依赖编译器的行为。

我们编写主函数编译之后就能很容易的发现其效率的不同:

* ++i  创建一对象,销毁一对象 int main() { for (INT i = 0; i < 5;++i) { i.Print(); } }

*  i++  后置++返回的操作之前的一个副本值,需要不断的拷贝和销毁 int main() { for (INT i = 0; i < 5;i++)
{ i.Print(); } }

总结: 在非内置类型的时候,尽量使用前置++,因为效率高(后置自增,效率低)

点赞收藏是对博主最大的支持!感谢!

最近太忙了..

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信