我们的问题是,在C语言中i++和++i的性能有区别吗?

c++的答案是什么?


当前回答

是时候给人们提供智慧的宝石了;)-有一个简单的技巧可以让c++的后缀增量表现得和前缀增量几乎一样(为自己发明的,但我在其他人的代码中也看到了它,所以我不是一个人)。

基本上,诀窍是在返回后使用helper类来延迟增量,然后RAII来拯救

#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;
}

Invented用于一些繁重的自定义迭代器代码,它减少了运行时间。前缀vs后缀的成本现在是一个参考,如果这是自定义操作符做大量的移动,前缀和后缀产生了相同的运行时为我。

其他回答

Mark:只是想指出操作符++是很好的内联候选者,如果编译器选择这样做,在大多数情况下多余的拷贝将被消除。(例如POD类型,迭代器通常是这种类型。)

也就是说,在大多数情况下使用++iter仍然是更好的风格。: -)

是时候给人们提供智慧的宝石了;)-有一个简单的技巧可以让c++的后缀增量表现得和前缀增量几乎一样(为自己发明的,但我在其他人的代码中也看到了它,所以我不是一个人)。

基本上,诀窍是在返回后使用helper类来延迟增量,然后RAII来拯救

#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;
}

Invented用于一些繁重的自定义迭代器代码,它减少了运行时间。前缀vs后缀的成本现在是一个参考,如果这是自定义操作符做大量的移动,前缀和后缀产生了相同的运行时为我。

@Ketan

...加薪忽略了关于意图和性能的细节。有时我们想用iter++而不是++iter。

Obviously post and pre-increment have different semantics and I'm sure everyone agrees that when the result is used you should use the appropriate operator. I think the question is what should one do when the result is discarded (as in for loops). The answer to this question (IMHO) is that, since the performance considerations are negligible at best, you should do what is more natural. For myself ++i is more natural but my experience tells me that I'm in a minority and using i++ will cause less metal overhead for most people reading your code.

毕竟,这就是语言不叫“c++ C”的原因。[*]

[*]插入关于c++ C是一个更合乎逻辑的名称的必要讨论。

当您将操作符视为值返回函数以及它们的实现方式时,++i和i++之间的性能差异将更加明显。为了更容易理解发生了什么,下面的代码示例将使用int,就像它是一个结构体一样。

++i对变量加1,然后返回结果。这可以就地完成,并且只需要最少的CPU时间,在许多情况下只需要一行代码:

int& int::operator++() { 
     return *this += 1;
}

但是i++就不一样了。

后递增(i++)通常被视为在递增之前返回原始值。但是,函数只能在完成时返回结果。因此,有必要创建一个包含原始值的变量的副本,增加变量,然后返回包含原始值的副本:

int int::operator++(int& _Val) {
    int _Original = _Val;
    _Val += 1;
    return _Original;
}

当增量前和增量后之间没有功能差异时,编译器可以执行优化,使两者之间没有性能差异。但是,如果涉及到结构或类等复合数据类型,则在增量后调用复制构造函数,如果需要深度复制,则不可能执行此优化。因此,前增量通常比后增量更快,需要的内存更少。

我想指出Andrew Koenig最近在Code Talk上发表的一篇出色的文章。

http://dobbscodetalk.com/index.php?option=com_myblog&show=Efficiency-versus-intent.html&Itemid=29

在我们公司,我们也在适用的情况下使用++iter的一致性和性能。但Andrew提出了关于意图与性能的忽略细节。有时我们想用iter++而不是++iter。

所以,首先决定你的意图,如果pre或post不重要,那么使用pre,因为它将有一些性能优势,避免创建额外的对象并抛出它。