C++中的運算符重載詳解
1、引例
class Complex { private: double Real,Image; public: Complex():Real(0),Image(0) {} Complex(double r, double i) : Real(r),Image(i) {} ~Complex() {} }; int main() { Complex c1(1.2,2.3); Complex c2(45,56); Complex c3; c3 = c1.Add(c2); }
類非常簡單,下面我們要討論如何寫一個Add函數,使得兩個對象的屬性相加,返回一個新的對象。
第一種:
Complex::Complex Add(const Complex &c) { Complex co; co.Real = this->Real + c.Real; co.Image = this->Image + c.Image; return co; }
問題1:如何寫出最節省空間的Add函數?
第二種:
Complex::Complex Add(const Complex &c) const { return Complex(c.Real + this->Real, c.Image + this.Image); }
由於不需要改變調用對象的屬性值,我們給this指針添加const 修飾。
分析過程如下:
問題2:為什麼第一種方式不節省空間呢?
首先第一種的代碼比較繁瑣,並且在函數棧幀中又創建瞭一個對象(第3行)。並且函數類型是值傳遞類型(第6行),返回的是一個將亡值對象。那麼整個Add函數空間會產生兩個對象,造成空間的浪費。
第二中代碼創建的是無名對象,減少瞭一個co對象的創建,並且將無名對象直接作為將亡值對象傳遞給main函數中的c3。
問題3:我們能否將Add函數改為引用類型,這樣來減少將亡值對象的創建
Complex::Complex &Add(const Complex &c) const { return Complex(c.Real + this->Real, c.Image + this.Image); }
VS2019發現報錯,不能返回引用對象:
我們進行分析:
問題4:我們能否將這個Add函數名改為 + 運算符?
//Complex::Complex Add(const Complex &c) const Complex::Complex +(const Complex &c) const { Complex co; co.Real = this->Real + c.Real; co.Image = this->Image + c.Image; return co; } int main() { ... //c3 = c1.Add(c2); c3 = c1.+(c2); //將原先Add的地方改變為加號。 ... }
這樣使用,編譯器又會報錯,操作符不可作為一個有效的函數名來被使用。
問題5:如何使 +預算符 作為函數名使用?
這就引出瞭今天的關鍵,函數運算符重載。
在C++中,為瞭使操作符作為一個有效的函數名,我們在操作符前面添加一個operator。
Complex operator+(const Complex &c) const { return Complex(c.Real + this->Real,c.Image + this->Image); } int main() { Complex c1(1.2,2.3); Complex c2(10,10); Complex c3; c3 = c1 + c2; //上面一行實際上是 //c3 = c1.operator+ (c2); //c3 = operator+(&c1,c2); //編譯器還會經過一次編譯 }
前面幾篇博客已經分析瞭第15行的由來,是將c1的地址傳遞給this指針,將c2作為形參c的別名傳遞給函數。
2、類中自動建立的函數
在C++中,如果我們定義一個類,它會給我們自動創建六個缺省函數:
構造函數析構函數拷貝構造函數賦值函數普通對象的&(取地址符)的重載常對象的&(取地址符)重載
代碼示例如下:
class Object { public: Object() {} //構造函數 ~Object() {} //析構函數 Object(const Object &obj) {} //拷貝構造函數 Object &operator=() {const Object &obj} //賦值函數 { return *this; } Object *operator&() //普通對象的&(取地址符)的重載 { return this; } const Object *operator&() const //常對象的&(取地址符)重載 { return this; } };
然後,在C11標準下,又增添瞭兩個缺省函數,這裡不做深究:
移動構造函數移動賦值函數
3、重載賦值運算符解析
回到最初的例子:
class Object { int value; public: Object () { cout << "create:" << this << endl; } //普通構造函數 Object (int x = 0):value(x) {cout << "create:" << this << endl;} //缺省構造函數 ~Object() //析構函數 { cout << "~Objcet() " << this << endl; } Object(Object &obj):value(obj.value) { cout << "Copy create:" << this << endl; } int & Value() { return value; } const int &Value() const { return value; } Object &operator=(const Object& obj) //此處加引用 { this->value = obj.value; return *this; //this指針指向objb的地址。賦值函數結束後,objb不會被消亡,所以可以以引用返回 } void operator=(const Object& obj) //賦值語句不可給this指針加const { this->value = obj.value; } }; int main() { Object objx(0); Object objy(0); objy = fun(objx); cout << objy.Value() << endl; return 0; }
我們在34行添加一個等號運算符重載函數: void operator=(const Object& obj)
此處不可添加const修飾this指針,因為需要使用this指針作為左值被修改。
問題6:void operator=(const Object& obj) 隻能用於 obja = objb,為什麼不可以這樣使用 obja = objb = objc;
我們逐一分析:
obja = objb = objc; //當調用等號運算符函數的時候。 obja = objb.operator = (objc); obja = operator = (&objb,objc); //如果此處是調用的是 void operator=(const Object& obj) ; //等號從右向左指向,我們不能把一個void 類型賦給一個obja對象類型。
我們將賦值運算符進行再次重載,丟棄 void 版本:
Object &operator=(const Object& obj) //此處加引用 { this->value = obj.value; return *this; //this指針指向objb的地址。賦值函數結束後,objb不會被消亡,所以可以以引用返回 }
這樣就可以使用瞭。
我們接著上次的深入分析:
obja.operator=(operator=(&objb,objc)); operator=(&obja,operator=(&objb,objc));
問題7:如果遇到obja = obja這種情況,如何賦值呢?
回答:對this指針和形參引用進行判斷。
Object &operator=(const Object &obj) { if(this != &obj) { this->value = obj.value } }
問題8:為什麼函數是在棧區構建的,以引用返回打印的不是一個隨機值?
運行程序,VS2012中,打印的是一個隨機值。
VS2019打印的是一個正常值。
c));
> 問題7:如果遇到obja = obja這種情況,如何賦值呢? > > 回答:對this指針和形參引用進行判斷。 ```cpp Object &operator=(const Object &obj) { if(this != &obj) { this->value = obj.value } }
問題8:為什麼函數是在棧區構建的,以引用返回打印的不是一個隨機值?
運行程序,VS2012中,打印的是一個隨機值。
VS2019打印的是一個正常值。
在WIN10系統中,VS2019與操作系統完全結合,安全性更高。當程序多次運行的時候,它的邏輯地址都不一樣,這樣做的好處是:當病毒入侵時,由於程序的邏輯地址是變化的,病毒不好尋找入侵的入口。
總結
到此這篇關於C++中的運算符重載詳解的文章就介紹到這瞭,更多相關C++運算符重載內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- C++中的拷貝構造詳解
- 聊聊C++ 運算符重載知識
- 最新C/C++中的new和delete的實現過程小結
- C++編程面向對象入門全面詳解
- 淺析C++中dynamic_cast和static_cast實例語法詳解