C++性能之戰(1)--深入到彙編看++i、i++、i+=1、i=i+1的區別

0.  寫在最前面

希望大家收藏:

本文持續更新地址:https://haoqchen.site/2018/10/15/difference-between-++i-i++-i+=1-i=i+1/

面試被問到,上面這四個有什麼區別。總結了一下,如果覺得還不錯就點個贊,點個關注唄,博主會長期更新自己的學習和收穫。

目錄

0.  寫在最前面

1. 首先對於內置類型,對於現代編譯器而言,這四個的效率都是沒有區別的

2. 但是對於自定義類型,這就不一樣了。

2.1 a++與++a區別

2.1.1 效率檢測:

2.1.2 左右值檢測:

2.2 a+=b與a=a+b的區別

參考


 

  • 1. 首先對於內置類型,對於現代編譯器而言,這四個的效率都是沒有區別的

我在VS2013下進行編譯運行,然後調試->窗口->反彙編,查看彙編代碼後發現,這四個都是一樣的。。。。。

dword 雙字 就是四個字節
ptr pointer縮寫 即指針
[]裏的數據是一個地址值,這個地址指向一個雙字型數據
比如mov eax, dword ptr [12345678] 把內存地址12345678中的雙字型(32位)數據賦給eax寄存器。

  • 2. 但是對於自定義類型,這就不一樣了。

2.1 a++與++a區別

  1. a++是先賦值再自增,++a是先自增再賦值。
  2. a++是先用臨時對象保存原來的對象,然後對原對象自增,再返回臨時對象,不能作爲左值;++a是直接對於原對象進行自增,然後返回原對象的引用,可以作爲左值。
  3. 由於要生成臨時對象,a++需要調用兩次拷貝構造函數與析構函數(將原對象賦給臨時對象一次,臨時對象以值傳遞方式返回一次);++a由於不用生成臨時變量,且以引用方式返回,故沒有構造與析構的開銷,效率更高。

左值一般是可以放在賦值符號左邊的值,其在內存中有實體;右值一般只能放在賦值符號右邊,不具有內存實體,無法通過取地址獲得相應對象。

考慮如下類:

class Point{
    int x_;
    int y_;
public:
    Point(int x = 0, int y = 0);
    Point(const Point&);
    ~Point();
    Point& operator++();//前置
    const Point operator++(int);//後置
    Point operator+(const Point&);
    Point& operator+=(const Point&);
    void DisplayPoint();
};

Point& Point::operator+=(const Point& _right)
{
    this->x_ += _right.x_;
    this->y_ += _right.y_;
    return *this;
}

Point Point::operator+(const Point& _right)
{
    Point temp;
    temp.x_ = this->x_ + _right.x_;
    temp.y_ = this->y_ + _right.y_;
    return temp;
}


Point& Point::operator++()
{
    ++x_;
    ++y_;
    return *this;
}

const Point Point::operator++(int)
{
    Point temp(*this);
    this->x_++;
    this->y_++;
    return temp;
}

Point::Point(int x, int y)
{
    x_ = x;
    y_ = y;
    cout << "this is constructor" << endl;
}

Point::Point(const Point& b)
{
    this->x_ = b.x_;
    this->y_ = b.y_;
    cout << "this is copy constructor" << endl;
}

Point::~Point()
{
    cout << "this is destructor" << endl;
}

void Point::DisplayPoint()
{
    cout << "x: " << this->x_ << endl;
    cout << "y: " << this->y_ << endl;
}

2.1.1 效率檢測:

Point a(1,1);
cout << endl << "this is a++: " << endl;
a++;
cout << endl << "this is ++a: " << endl;
++a;

將會輸出:
可以看到,a++將會有兩次的拷貝構造與析構的調用,效率非常低。

2.1.2 左右值檢測:

Point b(2, 2);
Point* c;
cout << endl << "this is &b: " << &b << endl;

cout << endl << "this is c = &(++b): ";
c = &(++b);
cout << c << endl;

cout << endl << "this is c = &(b++): ";
c = &(b++);
cout << c << endl;

將會輸出:
可以看到++b返回的對象指針跟b原來的地址是一樣的,而b++返回的對象地址跟原來的b地址不一樣(應該是臨時對象的地址),雖然可以取到地址,但當成左值將有可能導致錯誤。比如b++ = c;就不會將c給b,達不到原來的目的。爲此,我們應該將後置的++函數返回值定義爲const類型,就可以避免這種當成左值情況出現:const Point Point::operator++(int)。另外發現返回temp的引用可以減少一次拷貝和析構,但是不建議返回局部變量的引用!!因爲函數退出,局部變量將析構,引用就會指向不確定內存。

另外不要在一條語句中使用多個++,因爲在不同系統中對這樣的情況處理可能不一樣,比如y = (4 + x++) + (6 + x++)。這條語句只能保證程序執行到下一條語句之前,x被遞增兩次,並不能保證4 + x++後立即自增。

2.2 a+=b與a=a+b的區別

a+=b返回的是a的引用,中間不涉及構造與析構,效率與++a一樣。而a=a+b則會生成臨時變量,而且以值傳遞方式返回,會有兩次的構造與析構,與a++一樣。

參考

i++和++i區別

《C++ Primer Plus》第六版P133

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章