詳解C++ 運(yùn)算符重載中返回值的坑
相信不少朋友在學(xué)習(xí)運(yùn)算符重載的時(shí)候,都會(huì)被參數(shù)與返回值應(yīng)該是左值引用,還是右值引用,還是const常量所困擾。當(dāng)然我無法一一枚舉,這次先講一下返回值的坑 (沒錯(cuò)就是我親手寫的bug)
E0334 “Myclass” 沒有適當(dāng)?shù)膹?fù)制構(gòu)造函數(shù)
其實(shí)這個(gè)問題的根源是,沒有定義常量參數(shù)類型的拷貝構(gòu)造函數(shù)所致
先來看看代碼
//頭文件head.h class Myclass { private: int a; public: Myclass(int b=0):a(b) {} //構(gòu)造函數(shù) Myclass(Myclass& c); //復(fù)制構(gòu)造函數(shù) ~Myclass(){} //析構(gòu)函數(shù) Myclass operator+(Myclass& d); //重載+運(yùn)算符 friend ostream& operator<<(ostream& os ,const Myclass& d); //重載<<運(yùn)算符 }; //以下是定義 Myclass::Myclass(Myclass& c) { a = c.a; } Myclass Myclass::operator+(Myclass& d) { return Myclass(d.a+a); //??!此處報(bào)錯(cuò) } ostream& operator<<(ostream& os,const Myclass& d) { os << d.a << std::endl; return os; } //main.cpp #include"head.h" int main() { Myclass a1(5); Myclass a2(12); Myclass sum = a1 + a2; //??!此處報(bào)錯(cuò) std::cout << sum; }
代碼在VS中,又出現(xiàn)了令人討厭的小紅線,沒有適當(dāng)?shù)膹?fù)制構(gòu)造函數(shù),這就有疑問了, 不是明明有個(gè)構(gòu)造函數(shù)Myclass(int b=0):a(b) {}嗎,參數(shù)是int很合適??? 于是,我們定義一個(gè)臨時(shí)變量temp,再將它返回,此時(shí)會(huì)隱式調(diào)用拷貝構(gòu)造函數(shù)而后返回一個(gè)副本后原來的temp就die了,因此返回值不可以是引用。下面是代碼
Myclass Myclass::operator+(Myclass& d) { Myclass temp(d.a + a); return temp; }
此時(shí)第一處報(bào)錯(cuò)消失了,但是第二處報(bào)錯(cuò)依然存在,而且仍為 “沒有適當(dāng)?shù)膹?fù)制構(gòu)造函數(shù)”,這就說明了,我的入手方向應(yīng)該是拷貝構(gòu)造函數(shù)
經(jīng)過博主的調(diào)試,得知是因?yàn)楹瘮?shù)的返回值是一個(gè)純右值,為了驗(yàn)證這個(gè)想法,使用了右值引用,來接收這個(gè)純右值(當(dāng)然,右值引用更多的是用在移動(dòng)構(gòu)造函數(shù)上,將 將亡值“偷”出來)
#include"head.h" int main() { Myclass a1(5); Myclass a2(12); Myclass&& sum = a1 + a2; }
果然,它不報(bào)錯(cuò)了
但是考慮到實(shí)用性,總不能讓用戶今后做個(gè)加法都要用右值引用接收吧,因此,我們要從源頭解決,即重載拷貝構(gòu)造函數(shù)。
值得思考的是,右值不就是被賦值的那個(gè)嗎,為什么用Myclass&& sum = a1 + a2;無法賦值呢?眾所周知,Myclass&& sum = a1 + a2;調(diào)用的是拷貝構(gòu)造函數(shù),類不同于基本數(shù)據(jù)類型,它要通過程序員來設(shè)置一系列的功能,我們沒有設(shè)置接受,Myclass類型的右值的功能,只定義了接受int類型的右值的功能,這自然是不行的了。
因此,重載拷貝構(gòu)造函數(shù)
Myclass::Myclass(const Myclass& c) { a = c.a; }
此時(shí)就能運(yùn)行了
E0349 沒有與這些操作數(shù)匹配的 “<<” 運(yùn)算符
關(guān)于流運(yùn)算符為什么要寫成$ostream& operator<<(ostream& os,const Myclass& d); 而非ostream& operator<<(ostream& os,Myclass& d);這個(gè)問題,網(wǎng)上絕大部分的回答都是輸出沒必要修改值。那么我們先定義后者
#head.h #pragma once #include<iostream> using std::ostream; class Myclass { private: int a; public: Myclass(int b=0):a(b) {} Myclass(Myclass& c); Myclass(const Myclass& c); ~Myclass(){} Myclass operator+(Myclass& d); friend ostream& operator<<(ostream& os ,Myclass& d); }; Myclass::Myclass(const Myclass& c) { a = c.a; } Myclass::Myclass(Myclass& c) { a = c.a; } Myclass Myclass::operator+(Myclass& d) { Myclass temp(d.a + a); return temp; } ostream& operator<<(ostream& os,Myclass& d) { os << d.a << std::endl; return os; } #main.cpp #include"head.h" int main() { Myclass a1(5); Myclass a2(12); Myclass&& sum = a1 + a2; std::cout << a1 + a2; //此處有討厭小紅線 }
不難發(fā)現(xiàn),討厭的小紅線又出來了。
我們可以想象一下這個(gè)過程,a1.operator+(a2),返回了個(gè)臨時(shí)變量,暫且假設(shè)它叫newguy,那么newguy為一個(gè)右值,又調(diào)用了函數(shù)os.<<(&d),傳參為&d=newguy,現(xiàn)在問題來了,左值引用怎么能夠接受一個(gè)純右值呢? 而我們定義的重載的流運(yùn)算符接受的參數(shù)類型為左值,我們并沒有給出從左值到右值強(qiáng)制類型轉(zhuǎn)換的函數(shù),但是在上一部分,我們給出了從右值到左值的拷貝構(gòu)造函數(shù),因此,將流運(yùn)算符聲明為前者更好。
C3861 “function”: 找不到標(biāo)識(shí)符
這個(gè)問題應(yīng)該是非常常見的,不習(xí)慣將函數(shù)(或是類)先聲明后定義而又喜歡讓函數(shù)(或是類)相互調(diào)用,但是在類模板它比以上兩種更為隱蔽。
#include<iostream> class A { friend void show(); //“聲明”函數(shù) friend void show1(); //“聲明”函數(shù) }; void show() //定義 { show1(); } void show1(){} //定義 int main() { A a; show(); //調(diào)用 show1(); //調(diào)用 }
以上流程看似聲明->定義->調(diào)用非常完美,實(shí)則還是會(huì)報(bào)錯(cuò)的,不過跟以上兩種不一樣的是,它是在linking的時(shí)候出錯(cuò),這是為什么呢?
原來友元函數(shù)并不屬于這個(gè)類的一部分,在類內(nèi)定義僅僅是為了告訴編譯器“這個(gè)函數(shù)是這個(gè)類的友元函數(shù)”,并沒有對(duì)這個(gè)函數(shù)本身進(jìn)行聲明,因此,正確的做法應(yīng)該是這樣的:
#include<iostream> void show(); void show1(); class A { friend void show(); friend void show1(); }; void show() { show1(); } void show1(){} int main() { A a; show(); show1(); }
總結(jié)
本文主要講了三點(diǎn)。
首先,要注意將拷貝構(gòu)造函數(shù)重載。
其次,要將流運(yùn)算符<<的參數(shù)類型確定為(ostream&,const myclass&),當(dāng)然,istream則萬(wàn)萬(wàn)不可const,ostream是沒有拷貝構(gòu)造函數(shù)的,因此引用也是必須的。
最后,類內(nèi)友元函數(shù)的聲明,并不等同于函數(shù)本身的聲明。
到此這篇關(guān)于詳解C++ 運(yùn)算符重載中返回值的坑的文章就介紹到這了,更多相關(guān)C++ 運(yùn)算符重載返回值內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言動(dòng)態(tài)內(nèi)存分配的詳解
這篇文章主要介紹了C語(yǔ)言動(dòng)態(tài)內(nèi)存分配的詳解的相關(guān)資料,這里提供了實(shí)現(xiàn)方法整理和出現(xiàn)錯(cuò)誤的解決辦法,需要的朋友可以參考下2017-07-07利用C語(yǔ)言實(shí)現(xiàn)HashTable
根據(jù)KEY從hashtable中獲取接點(diǎn),步驟是先根據(jù)KEY計(jì)算hash值,然后從hashtable中找到指定的接點(diǎn)或者接點(diǎn)鏈表2013-09-09C++ 數(shù)據(jù)結(jié)構(gòu)之kmp算法中的求Next()函數(shù)的算法
這篇文章主要介紹了C++ 數(shù)據(jù)結(jié)構(gòu)之kmp算法中的求Next()函數(shù)的算法的相關(guān)資料,需要的朋友可以參考下2017-06-06