C++ 请以pass-by-reference-to-const替换pass-by-value
20180312 C++ 請(qǐng)以pass-by-reference-to-const替換pass-by-value
缺省情況下C++以by value方式(一個(gè)繼承自C的方式)傳遞對(duì)象至(或來自)函數(shù),除非你另外指定,否則函數(shù)參數(shù)就是以實(shí)際實(shí)參的復(fù)件(副本)為初值,而調(diào)用端所獲得的亦是函數(shù)返回值的一個(gè)復(fù)件。這些復(fù)件(副本)是由對(duì)象的copy構(gòu)造函數(shù)產(chǎn)出,這可能使得pass-by-value成為昂貴的(費(fèi)時(shí)的)操作??紤]以下class繼承體系:
class Person{
public:
? Person(); ? ? ? ? //為求簡化,省略參數(shù)
? virtual ~Person();
? ...
private:
? std::string name;
? std::string address;
};
class Student: public Person{
public:
? Student();
? ~Student();
? ...
private:
? std::string schoolName;
? std:;string schoolAddress;
};
現(xiàn)在考慮以下代碼,其中調(diào)用函數(shù)validateStudent,后者需要一個(gè)Student實(shí)參(by value)并返回它是否有效:
bool validateStudent(Student s);//函數(shù)以by value方式接受學(xué)生
Student yanhui;//顏回,孔子的學(xué)生
bool yanhuiisOK = validateStudent(yanhui);//調(diào)用函數(shù)
上述函數(shù)被調(diào)用時(shí),無疑的,Student的copy構(gòu)造函數(shù)會(huì)被調(diào)用,以yanhui為藍(lán)本將顏回初始化。同樣明顯地,當(dāng)validateStudent返回s會(huì)被銷毀。因此,對(duì)于函數(shù)而言,參數(shù)的傳遞成本是“一次Student 拷貝構(gòu)造函數(shù)調(diào)用,加上一次Student析構(gòu)函數(shù)被調(diào)用”。
詳細(xì)過程為:Student對(duì)象內(nèi)有兩個(gè)string對(duì)象,所以每次構(gòu)造一個(gè)Student對(duì)象也就構(gòu)造了兩個(gè)string對(duì)象,此Student對(duì)象繼承自Person對(duì)象,所以每次構(gòu)造Student對(duì)象也必須構(gòu)造出一個(gè)Person對(duì)象。一個(gè)Person對(duì)象又有兩個(gè)string對(duì)象在其中,因此每一次Person構(gòu)造動(dòng)作又需承擔(dān)兩個(gè)string構(gòu)造動(dòng)作。最終結(jié)果是,以by value方式傳遞一個(gè)Student對(duì)象會(huì)導(dǎo)致調(diào)用一次Student 拷貝構(gòu)造函數(shù),一次Person拷貝構(gòu)造函數(shù),四次string拷貝構(gòu)造函數(shù)。當(dāng)函數(shù)內(nèi)的那個(gè)Student復(fù)件被銷毀,每一個(gè)構(gòu)造函數(shù)調(diào)用動(dòng)作都需要一個(gè)對(duì)應(yīng)的析構(gòu)函數(shù)調(diào)用動(dòng)作。因此,以by value方式傳遞一個(gè)Student對(duì)象,總體成本是“六次構(gòu)造函數(shù)和六次析構(gòu)函數(shù)”。
如果想出一個(gè)方法可以回避所有構(gòu)造和析構(gòu)函數(shù)就好了。有的!就是pass by reference-to-const:
bool validateStudent(const Student& s);
現(xiàn)在Student以by reference方式傳遞,將它聲明為const是必要的,因?yàn)椴贿@樣做的話調(diào)用者會(huì)憂慮validateStudent會(huì)不會(huì)改變他們傳入的那個(gè)Student。
以by reference方式傳遞參數(shù)也可以避免對(duì)象切割(slicing)問題。假設(shè)在一組classes上工作,用來實(shí)現(xiàn)一個(gè)圖形窗口系統(tǒng):
class Window{
public:
? ...
? std::string name() const;//返回窗口名稱
? virtual void display() const;//顯示窗口和其他內(nèi)容
};
class WindowWithScrollBars:public Window{
public:
? ...
? virtual void display() const;
};
所有Window對(duì)象都帶有一個(gè)名稱,你可以通過name函數(shù)取得它。所有窗口都可顯示,你可以通過display函數(shù)完成它。display是一個(gè)virtual函數(shù),這意味著簡易樸素的基類Window對(duì)象的顯示方式和華麗WindowWithScrollBars對(duì)象的顯示方式不同。(派生類是基類的擴(kuò)展)。
現(xiàn)在假設(shè)寫個(gè)函數(shù)打印窗口名稱,然后顯示窗口,下面的示范是錯(cuò)誤的:
void printNameAndDisplay(Window w)//不正確! 參數(shù)可能被切割
{
? std::cout<<w.name();
? w.display();
}
當(dāng)調(diào)用上述函數(shù)并交給他WindowWithScrollingBars對(duì)象 會(huì)怎樣呢?
WindowWithScrollingBars wwsb;
printNameAndDiaplay(wwsb);
參數(shù)w會(huì)被構(gòu)造成一個(gè)Window對(duì)象;它是pass by value,而造成wwsb“是個(gè)WindowWithScrollingBars對(duì)象”的所有特化信息都會(huì)被切除。在printNameAndDisplay函數(shù)內(nèi)不論傳遞過來的對(duì)象原本是什么類型,參數(shù)w就像一個(gè)Window對(duì)象(因?yàn)槠漕愋偷腤indow)。因此在printNameAndDisplay內(nèi)調(diào)用display掉用的總是Window::display,絕對(duì)不會(huì)是WindowWithScrollingBars::diaplay。
解決切割(slicing)問題的辦法,就是以by reference-to-const的方式傳遞w:
void printNameAndDisplay(const Window& w)//很好,參數(shù)不會(huì)被切割
{
? std::cout<<w.name;
? w.dispaly();
}
現(xiàn)在,傳進(jìn)來的窗口是什么類型,w就表現(xiàn)出那種類型。
在C++底層實(shí)現(xiàn)中,reference往往以指針實(shí)現(xiàn),因此pass by reference通常意味著真正傳遞的是指針,因此若對(duì)象屬于內(nèi)置類型,pass by value往往比pass by reference效率更高,這種情況也適用于STL的迭代器和函數(shù)對(duì)象。
注意:
1、盡量以pass-by-reference-to-const替換pass-by-value。前者通常比較高效,并可以避免切割問題(slicing problem)。
2、以上規(guī)則并不適用于內(nèi)置類型,以及STL的迭代器和函數(shù)對(duì)象。對(duì)它們而言,pass-by-value往往比較合適。
總結(jié)
以上是生活随笔為你收集整理的C++ 请以pass-by-reference-to-const替换pass-by-value的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 黑苹果hidp显示不清楚_macOS 2
- 下一篇: MTK 8735A 8.1 自定义按键