C++ i++;和++;我在C++;?
我们有一个问题C++ i++;和++;我在C++;?,c++,performance,oop,post-increment,pre-increment,C++,Performance,Oop,Post Increment,Pre Increment,我们有一个问题 < C++ > 的答案是什么?[执行摘要:如果没有具体的原因使用 I++< /COD> 对于C++,答案是有点复杂。 < > > 是一个简单类型(不是C++类的实例),因为编译器生成代码,所以保持/< 但是,如果 i 是C++类的一个实例,那么 i++> /COD>和 ++i 正在调用一个函数。以下是这些函数的标准对: Foo& Foo::operator++() // called for ++i { this->data += 1; re
< C++ > 的答案是什么?[执行摘要:如果没有具体的原因使用<代码> I++< /COD> 对于C++,答案是有点复杂。 < > <代码> > 是一个简单类型(不是C++类的实例),因为编译器生成代码,所以保持/< <>但是,如果<代码> i <代码>是C++类的一个实例,那么<代码> i++> /COD>和
Foo& Foo::operator++() // called for ++i
{
this->data += 1;
return *this;
}
Foo Foo::operator++(int ignored_dummy_value) // called for i++
{
Foo tmp(*this); // variable "tmp" cannot be optimized away by the compiler
++(*this);
return tmp;
}
由于编译器不生成代码,而只是调用运算符+++
函数,因此无法优化tmp
变量及其关联的复制构造函数。如果复制构造函数很昂贵,那么这可能会对性能产生重大影响。是。有
++运算符可以定义为函数,也可以不定义为函数。对于基本类型(int,double,…),操作符是内置的,因此编译器可能能够优化代码。但是对于定义++运算符的对象,情况就不同了
运算符++(int)函数必须创建一个副本。这是因为postfix++预期返回的值与它所保留的值不同:它必须在temp变量中保留其值,增加其值并返回temp。对于操作符++(),前缀++,不需要创建副本:对象可以增加自身,然后简单地返回自身
以下是这一点的说明:
struct C
{
C& operator++(); // prefix
C operator++(int); // postfix
private:
int i_;
};
C& C::operator++()
{
++i_;
return *this; // self, no copy created
}
C C::operator++(int ignored_dummy_value)
{
C t(*this);
++(*this);
return t; // return a copy
}
每次调用运算符++(int)时,都必须创建一个副本,而编译器对此无能为力。选择时,使用运算符++();这样您就不会保存副本。在许多增量(大循环?)和/或大对象的情况下,这可能很重要。说编译器不能在后缀情况下优化掉临时变量副本并不完全正确。用VC进行的快速测试表明,它至少在某些情况下可以做到这一点 在以下示例中,生成的前缀和后缀代码相同,例如:
#include <stdio.h>
class Foo
{
public:
Foo() { myData=0; }
Foo(const Foo &rhs) { myData=rhs.myData; }
const Foo& operator++()
{
this->myData++;
return *this;
}
const Foo operator++(int)
{
Foo tmp(*this);
this->myData++;
return tmp;
}
int GetData() { return myData; }
private:
int myData;
};
int main(int argc, char* argv[])
{
Foo testFoo;
int count;
printf("Enter loop count: ");
scanf("%d", &count);
for(int i=0; i<count; i++)
{
testFoo++;
}
printf("Value: %d\n", testFoo.GetData());
}
因此,虽然后缀版本的速度肯定会慢一些,如果您不使用临时副本,那么优化器很可能会很好地消除它。即使在没有性能优势的内置类型上,您也应该使用++i的原因是为自己创造一个好习惯。@wilhelmtell 编译器可以省略临时代码。来自另一个线程的逐字记录:
允许C++编译器消除基于栈的临时性,即使这样做改变了程序行为。VC 8的MSDN链接:
报纸上说: 预增量和预减量 将递增运算符和递减运算符的前缀形式(++i)与 迭代器和其他模板对象 定义:当变量递增(++i或i++)或递减(--i或 我--)并且表达式的值没有被使用,我们必须决定 是预递增(递减)还是后递增(递减) 优点:当忽略返回值时,“pre”形式(++i)永远不会减少 比“post”表单(i++)更高效,并且通常更高效。 这是因为后期增量(或减量)需要一个i到的副本 ,这是表达式的值。如果我是迭代器 其他非标量类型,复制i可能会很昂贵。自从两人 当忽略该值时,增量类型的行为相同,为什么不呢 总是预先增加 缺点:在C语言中,当 未使用表达式值,尤其是在for循环中。一些发现 后增量更易于阅读,因为“主题”(i)位于 “动词”(++),就像英语一样 决策:对于简单标量(非对象)值,没有理由选择一个 形式,我们允许任何一种。对于迭代器和其他模板类型,使用 增量前我想指出Andrew Koenig最近在Code Talk上发表的一篇精彩文章 在我们公司,在适用的情况下,我们还使用++国际热核聚变实验堆的一致性和性能惯例。但Andrew提出了关于意图与性能的过度关注的细节。有时我们希望使用iter++而不是iter++ 因此,首先确定您的意图,如果pre或post不重要,那么使用pre,因为它将通过避免创建额外对象并将其抛出而获得一些性能优势。@Ketan …提出了有关意图与性能的详细信息。有时我们希望使用iter++而不是iter++ 显然,post和pre-increment具有不同的语义,我相信每个人都同意,当使用结果时,应该使用适当的运算符。我认为问题是当结果被丢弃时(如
for
循环)应该怎么做。这个问题(IMHO)的答案是,由于性能考虑最多可以忽略不计,所以您应该做更自然的事情。对于我自己来说,++i
更自然,但我的经验告诉我,我是少数人,使用i++
会减少大多数人阅读代码的金属开销
毕竟,这就是为什么这种语言没有被称为“++C
”[*]
[*]插入关于
++C
是一个更符合逻辑的名称的强制性讨论 Mark:我只是想指出,操作符+++是内联的很好的候选对象,如果编译器选择这样做,在大多数情况下,冗余副本将被消除。(例如,迭代器通常是POD类型。)
也就是说,在大多数情况下使用++iter仍然是更好的风格。:-) 预期的问题是关于结果何时未使用(这从
for(int i=0; i<10; i++)
{
testFoo++;
}
printf("Value: %d\n", testFoo.GetData());
00401000 push 0Ah
00401002 push offset string "Value: %d\n" (402104h)
00401007 call dword ptr [__imp__printf (4020A0h)]
#include <stdio.h>
int main()
{
int a = 0;
a++;
int b = 0;
++b;
return 0;
}
0x0000000100000f24 <main+0>: push %rbp
0x0000000100000f25 <main+1>: mov %rsp,%rbp
0x0000000100000f28 <main+4>: movl $0x0,-0x4(%rbp)
0x0000000100000f2f <main+11>: incl -0x4(%rbp)
0x0000000100000f32 <main+14>: movl $0x0,-0x8(%rbp)
0x0000000100000f39 <main+21>: incl -0x8(%rbp)
0x0000000100000f3c <main+24>: mov $0x0,%eax
0x0000000100000f41 <main+29>: leaveq
0x0000000100000f42 <main+30>: retq
// a.cc
#include <ctime>
#include <array>
class Something {
public:
Something& operator++();
Something operator++(int);
private:
std::array<int,PACKET_SIZE> data;
};
int main () {
Something s;
for (int i=0; i<1024*1024*30; ++i) ++s; // warm up
std::clock_t a = clock();
for (int i=0; i<1024*1024*30; ++i) ++s;
a = clock() - a;
for (int i=0; i<1024*1024*30; ++i) s++; // warm up
std::clock_t b = clock();
for (int i=0; i<1024*1024*30; ++i) s++;
b = clock() - b;
std::cout << "a=" << (a/double(CLOCKS_PER_SEC))
<< ", b=" << (b/double(CLOCKS_PER_SEC)) << '\n';
return 0;
}
// b.cc
#include <array>
class Something {
public:
Something& operator++();
Something operator++(int);
private:
std::array<int,PACKET_SIZE> data;
};
Something& Something::operator++()
{
for (auto it=data.begin(), end=data.end(); it!=end; ++it)
++*it;
return *this;
}
Something Something::operator++(int)
{
Something ret = *this;
++*this;
return ret;
}
Flags (--std=c++0x) ++i i++
-DPACKET_SIZE=50 -O1 1.70 2.39
-DPACKET_SIZE=50 -O3 0.59 1.00
-DPACKET_SIZE=500 -O1 10.51 13.28
-DPACKET_SIZE=500 -O3 4.28 6.82
// c.cc
#include <array>
class Something {
public:
Something& operator++();
Something operator++(int);
private:
std::array<int,PACKET_SIZE> data;
};
Something& Something::operator++()
{
return *this;
}
Something Something::operator++(int)
{
Something ret = *this;
++*this;
return ret;
}
Flags (--std=c++0x) ++i i++
-DPACKET_SIZE=50 -O1 0.05 0.74
-DPACKET_SIZE=50 -O3 0.08 0.97
-DPACKET_SIZE=500 -O1 0.05 2.79
-DPACKET_SIZE=500 -O3 0.08 2.18
-DPACKET_SIZE=5000 -O3 0.07 21.90
int& int::operator++() {
return *this += 1;
}
int int::operator++(int& _Val) {
int _Original = _Val;
_Val += 1;
return _Original;
}
x = i++; // x contains the old value of i
y = ++i; // y contains the new value of i
#include <stdio.h>
int main(){
int a = 0;
printf("%d", a++);
printf("%d", ++a);
return 0;
}
#include <iostream>
using namespace std;
int main(){
int a = 0;
cout << a++;
cout << ++a;
return 0;
}
#include <iostream>
class Data {
private: class DataIncrementer {
private: Data& _dref;
public: DataIncrementer(Data& d) : _dref(d) {}
public: ~DataIncrementer() {
++_dref;
}
};
private: int _data;
public: Data() : _data{0} {}
public: Data(int d) : _data{d} {}
public: Data(const Data& d) : _data{ d._data } {}
public: Data& operator=(const Data& d) {
_data = d._data;
return *this;
}
public: ~Data() {}
public: Data& operator++() { // prefix
++_data;
return *this;
}
public: Data operator++(int) { // postfix
DataIncrementer t(*this);
return *this;
}
public: operator int() {
return _data;
}
};
int
main() {
Data d(1);
std::cout << d << '\n';
std::cout << ++d << '\n';
std::cout << d++ << '\n';
std::cout << d << '\n';
return 0;
}