一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

c 中有些重載運(yùn)算符為什么要返回引用

 奔跑的瓦力 2019-02-16

  事實(shí)上,我們的重載運(yùn)算符返回void、返回對象本身、返回對象引用都是可以的,并不是說一定要返回一個(gè)引用,只不過在不同的情況下需要不同的返回值。

那么什么情況下要返回對象的引用呢?

原因有兩個(gè):

  •   允許進(jìn)行連續(xù)賦值
  •       防止返回對象(返回對象也可以進(jìn)行連續(xù)賦值(常規(guī)的情況,如a = b = c,而不是(a = b) = c))的時(shí)候調(diào)用拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù)導(dǎo)致不必要的開銷,降低賦值運(yùn)算符的效率。

  

  對于第二點(diǎn)原因:如果用”值傳遞“的方式,雖然功能仍然正確,但由于return語句要把*this拷貝到保存返回值的外部存儲(chǔ)單元之中,增加了不必要的開銷,會(huì)降低賦值函數(shù)的效率。

 

  場景:

  需要返回對象引用或者返回對象(效率沒有返回引用高),需要實(shí)現(xiàn)連續(xù)賦值,使重載的運(yùn)算符更符合C++本身的運(yùn)算符語意,如連續(xù)賦值 = += -= *= 、=,<<輸出流

  關(guān)于賦值 =,我們知道賦值=有連續(xù)等于的特性

1 int x,y,z;2 x=y=z=15;

  同樣有趣的是,賦值采用的是右結(jié)合律,所以上述連鎖賦值被解析為

1 x=(y=(z=15));//賦值連鎖形式

  這里15先被賦值給z,然后其結(jié)果(更新后的z)再被賦值給y,然后其結(jié)果(更新后的y)再被賦值給x。

  為了實(shí)現(xiàn)”連鎖賦值“,賦值操作符號返回一個(gè)reference(引用)指向操作符號的左側(cè)實(shí)參(而事實(shí)上重載運(yùn)算符的左側(cè)實(shí)參就是調(diào)用對象本身,比如= += -=等),這是你為classes實(shí)現(xiàn)賦值操作符時(shí)應(yīng)該遵循的協(xié)議:這個(gè)協(xié)議不僅僅適用于以上的標(biāo)準(zhǔn)賦值形式,也適用于所有賦值運(yùn)算。

1 class Widght{ 2 public: 3 ..... 4 Widget& operator=(cosnt Widget& rhs) 5 { 6 ... 7 return* this; 8 } 9 Widget& operator+=(cosnt Widget& rhs)10 {11 ...12 return* this;13 } 14 15 Widget& operator-=(cosnt Widget& rhs)16 {17 ...18 return* this;19 } 20 21 Widget& operator*=(cosnt Widget& rhs)22 {23 ...24 return* this;25 } 26 27 Widget& operator/=(cosnt Widget& rhs)28 {29 ...30 return* this;31 } 32 ...33 };

  注意,這只是個(gè)協(xié)議,并無強(qiáng)制性,如果不遵循它,代碼一樣可以通過編譯,然而這份協(xié)議被所有內(nèi)置類型和標(biāo)準(zhǔn)程序庫提供的類型入string,vector,complex,std::trl::shared_ptr或者即將提供的類型共同遵守。因此除非你有一個(gè)標(biāo)新立異的好理由,不然還是隨眾吧。

 

  下面看一個(gè)賦值運(yùn)算符重載的例子:(連續(xù)賦值,常規(guī)的情況(a = b = c)

  1、首先是返回對象的情況:

 1 #include <iostream> 2 using namespace std; 3 class String 4 { 5 private: 6     char *str; 7     int len; 8 public: 9     String(const char* s);//構(gòu)造函數(shù)聲明10     String operator=(const String& another);//運(yùn)算符重載,此時(shí)返回的是對象11     void show()12     {13         cout << 'value = ' << str << endl;14     }15 16     /*copy construct*/17     String(const String& other)18     {19         len = other.len;20         str = new char[len + 1];21         strcpy(str, other.str);22         cout << 'copy construct' << endl;23     }24 25     ~String()26     {27         delete[] str;28         cout << 'deconstruct' << endl;29     }30 };31 32 String::String(const char* s)//構(gòu)造函數(shù)定義33 {34     len = strlen(s);35     str = new char[len + 1];36     strcpy(str, s);37 }38 39 String String::operator=(const String &other)//運(yùn)算符重載40 {41     if (this == &other)42         return *this;43 //        return;44     delete[] str;45     len = other.len;46     str = new char[len + 1];47     strcpy(str, other.str);48     return *this;49 //    return;50 }51 52 int main()53 {54     String str1('abc');55     String str2('123');56     String str3('456');57     str1.show();58     str2.show();59     str3.show();60     str3 = str1 = str2;//str3.operator=(str1.operator=(str2))    61     str3.show();62     str1.show();63     return 0;64 }

  運(yùn)行結(jié)果:

  

 

  2、下面是返回引用的情況(String& operator = (const String& str)),直接貼運(yùn)行結(jié)果:

  

  

  當(dāng)運(yùn)算符重載返回的是對象時(shí),會(huì)在連續(xù)賦值運(yùn)算過程的返回途中,調(diào)用兩次拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù)(因?yàn)閞eturn的是個(gè)新的對象)

  如果采用String& operator = (const String& str)這樣就不會(huì)有多余的調(diào)用(因?yàn)檫@里直接return一個(gè)已經(jīng)存在的對象的引用

  上面的栗子也說明一點(diǎn):析構(gòu)函數(shù)的調(diào)用是在變量作用域結(jié)束的時(shí)候(以及程序運(yùn)行結(jié)束的時(shí)候)

  如果采用return對象,那么第二次賦值運(yùn)算調(diào)用的情況就是

  將一個(gè)新的String對象(returnStringObj)傳遞到operator = (const String& str)的參數(shù)中去 相當(dāng)于 

const String&str = returnStringObj;

  如果采用return對象引用,那么第二次賦值運(yùn)算的情況就是

  將一個(gè)已經(jīng)存在的String對象的引用((其實(shí)就是str1))傳遞給operator = (const String& str)的參數(shù)中去

const String&str = returnReference; //(String& returnReference = str1;)

  +=等運(yùn)算符也是同樣的考慮,比如

1 int main() 2 { 3 String str1('abc'); 4 String str2('123'); 5 String str3('456'); 6 str1.show(); 7 str2.show(); 8 str3.show(); 9 str3 = str1 = str2;//str3.operator=(str1.operator=(str2)) 10 str3.show();11 str1.show();12 13 int num = 10;14 num += (num += 100);15 cout << num << endl;16 return 0;17 }

  

  如果使用+=或其它上面舉出的運(yùn)算符進(jìn)行連續(xù)操作時(shí),,則這些運(yùn)算符的返回值一定要是一個(gè)對象或者引用才行,不然就會(huì)出現(xiàn)錯(cuò)誤(參數(shù)類型不符合)。什么意思呢,下面舉個(gè)栗子。

  我現(xiàn)在讓運(yùn)算符重載返回的類型為空,單個(gè)賦值,不使用連續(xù)賦值

 1 #include <iostream> 2 using namespace std; 3 class String 4 { 5 private: 6     char *str; 7     int len; 8 public: 9     String(const char* s);//構(gòu)造函數(shù)聲明10     void operator=(const String& another);//運(yùn)算符重載,此時(shí)返回為空11     void show()12     {13         cout << 'value = ' << str << endl;14     }15 16     /*copy construct*/17     String(const String& other)18     {19         len = other.len;20         str = new char[len + 1];21         strcpy(str, other.str);22         cout << 'copy construct' << endl;23     }24 25     ~String()26     {27         delete[] str;28         cout << 'deconstruct' << endl;29     }30 };31 32 String::String(const char* s)33 {34     len = strlen(s);35     str = new char[len + 1];36     strcpy(str, s);37 }38 39 void String::operator=(const String &other)40 {41     if (this == &other)42 //        return *this;43         return;44     delete[] str;45     len = other.len;46     str = new char[len + 1];47     strcpy(str, other.str);48 //    return *this;49     return;50 }51 52 int main()53 {54     String str1('abc');55     String str2('123');56     String str3('456');57     str1.show();58     str2.show();59     str3.show();60     str3 = str1;//這樣OK61     str3.show();62     str1.show();63     return 0;64 }

  運(yùn)行結(jié)果:

   

  但當(dāng)我把主函數(shù)中str1,str2,str3改為連續(xù)賦值時(shí):

1 int main() 2 { 3 String str1('abc'); 4 String str2('123'); 5 String str3('456'); 6 str1.show(); 7 str2.show(); 8 str3.show(); 9 str3 = str1=str2;//這樣不OK10 str3.show();11 str1.show();12 return 0;13 }

  出錯(cuò):

  

  所以,當(dāng)你確定不使用連續(xù)賦值時(shí),直接返回void也是可以的。要明白一點(diǎn):

  運(yùn)算符左側(cè)的對象就是操作對象,比如

1 ObjectA = ObjectB 等同于ObjectA.operator=(ObjectB) 2 ObjectA+=ObjectB 等同于ObjectA.operator+(ObjectB)

  

  最后要說明一點(diǎn):并非必須返回引用,返回引用的好處既可以避免拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用,又可以保證= +=等運(yùn)算符的原始語義清晰。

  啥叫原始語義清晰呢?

  

1 (str3 = str1) = str2;

  我們的意識(shí)里,就是先執(zhí)行括號內(nèi)容,即str1賦值給str3,然后str2再賦值給str3,最后str3輸出的內(nèi)容是str2的。

  即如果運(yùn)算符重載返回的是對象引用時(shí),

 1 //返回的是對象引用的情況 2 #include <iostream> 3 using namespace std; 4 class String 5 { 6 private: 7     char *str; 8     int len; 9 public:10     String(const char* s);//構(gòu)造函數(shù)聲明11     String& operator=(const String& another);//運(yùn)算符重載,此時(shí)返回為引用12     void show()13     {14         cout << 'value = ' << str << endl;15     }16 17     /*copy construct*/18     String(const String& other)19     {20         len = other.len;21         str = new char[len + 1];22         strcpy(str, other.str);23         cout << 'copy construct' << endl;24     }25 26     ~String()27     {28         delete[] str;29         cout << 'deconstruct' << endl;30     }31 };32 33 String::String(const char* s)34 {35     len = strlen(s);36     str = new char[len + 1];37     strcpy(str, s);38 }39 40 String& String::operator=(const String &other)41 {42     if (this == &other)43         return *this;44 //        return;45     delete[] str;46     len = other.len;47     str = new char[len + 1];48     strcpy(str, other.str);49     return *this;50 //    return;51 }52 53 int main()54 {55     String str1('abc');56     String str2('123');57     String str3('456');58     str1.show();59     str2.show();60     str3.show();61     (str3 = str1) = str2;62     cout << 'str3的內(nèi)容為:' << endl;63     str3.show();64     return 0;65 }

  運(yùn)行結(jié)果:

  

   str3得到了str2的內(nèi)容,與我們認(rèn)識(shí)的‘=’運(yùn)算符邏輯相符。

  而如果運(yùn)算符重載返回的是對象時(shí),

1 //這是返回類型為對象的情況 2 #include <iostream> 3 using namespace std; 4 class String 5 { 6 private: 7 char *str; 8 int len; 9 public:10 String(const char* s);//構(gòu)造函數(shù)聲明11 String operator=(const String& another);//運(yùn)算符重載,此時(shí)返回為空12 void show()13 {14 cout << 'value = ' << str << endl;15 }16 17 /*copy construct*/18 String(const String& other)19 {20 len = other.len;21 str = new char[len + 1];22 strcpy(str, other.str);23 cout << 'copy construct' << endl;24 }25 26 ~String()27 {28 delete[] str;29 cout << 'deconstruct' << endl;30 }31 };32 33 String::String(const char* s)34 {35 len = strlen(s);36 str = new char[len + 1];37 strcpy(str, s);38 }39 40 String String::operator=(const String &other)41 {42 if (this == &other)43 return *this;44 // return;45 delete[] str;46 len = other.len;47 str = new char[len + 1];48 strcpy(str, other.str);49 return *this;50 // return;51 }52 53 int main()54 {55 String str1('abc');56 String str2('123');57 String str3('456');58 str1.show();59 str2.show();60 str3.show();61 (str3 = str1) = str2;62 cout << '賦值后str3的內(nèi)容為:' << endl;63 str3.show();64 return 0;65 }

 

  運(yùn)行結(jié)果:

  

  str3只得到了str1的內(nèi)容,并沒有得到str2的內(nèi)容,這是因?yàn)閳?zhí)行(str3=str1)后,因?yàn)榉祷氐氖菍ο螅?span>一個(gè)臨時(shí)對象,str3的一個(gè)拷貝),不是引用,所以此時(shí)str3不在后面的‘=str2’的操作中,而是str2對一個(gè)臨時(shí)對象賦值,所以str3的內(nèi)容保持不變(等于str1)。

 

  總結(jié)

  所以,對此類運(yùn)算符重載時(shí),還是老老實(shí)實(shí)的返回引用,少搞事,做個(gè)好男孩:)

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    久久中文字人妻熟女小妇| 亚洲另类女同一二三区| 青青草草免费在线视频| 欧美精品女同一区二区| 午夜国产精品国自产拍av| 婷婷基地五月激情五月| 亚洲最新av在线观看| 国产精品欧美激情在线观看| 国产传媒精品视频一区| 儿媳妇的诱惑中文字幕| 国产一级一片内射视频在线| 手机在线观看亚洲中文字幕| 一区二区三区欧美高清| 厕所偷拍一区二区三区视频| 国产精品偷拍视频一区| 久久99国产精品果冻传媒| 亚洲男人天堂成人在线视频| 免费观看在线午夜视频| 亚洲一区二区久久观看| 一区二区三区亚洲天堂| 黄片免费在线观看日韩| 麻豆果冻传媒一二三区| 欧美一级特黄特色大色大片| 中文字幕乱子论一区二区三区| 草草夜色精品国产噜噜竹菊| 精品久久综合日本欧美| 办公室丝袜高跟秘书国产| 老鸭窝精彩从这里蔓延| 国内外免费在线激情视频| 欧美日本亚欧在线观看| 沐浴偷拍一区二区视频| 国产中文另类天堂二区| 日本不卡一区视频欧美| 丰满少妇被猛烈插入在线观看| 亚洲av日韩av高潮无打码| 日本淫片一区二区三区| 美女黄色三级深夜福利| 国产一区二区三区免费福利| 亚洲五月婷婷中文字幕| 国产精品午夜福利免费阅读 | 中文字幕人妻一区二区免费 |