HOUR 13 Developing Advanced References and Pointer
阿新 • • 發佈:2018-03-18
std pri 變量 sage 周期 ren bob sky for
Passing by Reference for Efficiency
傳統的傳值方法,參數傳入時,執行一次copy,函數返回時,又一次copy執行,這樣很消耗內存,運行效率慢。C++引入了copy constructor 可以解決這個問題。
下面的代碼演示上面的這句話:定義一個SimpleCat類,創建兩個函數,分別通過pass by value 和 pass by reference OR pointer, 返回指針。 傳reference不需要復制對象,缺點是把原對象直接暴露在被調函數中。
#include <QCoreApplication> #include <iostream> classSimpleCat { public: SimpleCat(); SimpleCat(SimpleCat&); ~SimpleCat(); }; SimpleCat::SimpleCat() { std::cout << "SimpleCat Constructor Performed ..." << std::endl; } SimpleCat::SimpleCat(SimpleCat&) { std::cout << "SimpleCat Copy Constructor Performed ..." << std::endl; } SimpleCat::~SimpleCat() { std::cout << "SimpleCat Destructor Performed ..." << std::endl; } SimpleCat functionOne(SimpleCat srcCat); SimpleCat *functionTwo(SimpleCat *srcCat); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); std::cout<< "Making a cat ..." << std::endl; SimpleCat Frisky; std::cout << "\n\n\n\n\n"; std::cout << "\n\n\n\n\n"; std::cout << "Calling functionOne ..." << std::endl; functionOne(Frisky); std::cout << "\n\n\n\n\n"; std::cout << "\n\n\n\n\n"; std::cout << "Calling functionTwo ..." << std::endl; functionTwo(&Frisky); return a.exec(); } //FunctionOne pass by value SimpleCat functionOne(SimpleCat srcCat) { std::cout << "Function one. Returning ..." << std::endl; return srcCat; } //FunctionTwo pass by pointer SimpleCat *functionTwo(SimpleCat *srcCat) { std::cout << "Function two. Returning ..." << std::endl; return srcCat; }
執行結果如下圖:
函數1:
- main函數傳值給函數1,相當於復制一個object給被調函數,所以復制構造函數被執行->SimpleCat Copy Constructor Performed ... 這裏srcCat的存儲位置在stack
- 函數1執行-> function one returning
- 函數1返回object給主函數(傳值復制方式),再一次調用復制構造函數,把處理後的object復制給主函數。
- 第三步裏子函數向主函數復制object時,產生臨時對象(tempObject 我們看不見),在返回結束後被銷毀,所以執行了一次constructor和destructor
- 函數1返回後,該函數結束,srcCat 生命周期結束,調用destructor
函數2:
- 主函數向子函數傳遞參數使用reference,未調用constructor
- 子函數向主函數返回的也是reference(返回值srcCat實際上是Frisky的地址)
傳值方式,調用兩次復制構造函數和兩次析構函數,然後傳reference方式一次也不調用
Pass a const Pointer
為什麽?
上面的函數2,效率挺高,但是存在風險。直接把地址給了子函數,子函數可以為所欲為,通常主函數不想改變原始值。傳值方式提供了這種保護,不過太蠢了,效率太慢了。
既想要傳值方式的安全,又想要傳引用方式的效率——傳const pointer
#include <QCoreApplication> #include <iostream> class SimpleCat { public: SimpleCat(); SimpleCat(SimpleCat&); ~SimpleCat(); int getAge() const {return itsAge;} void setAge(int age) {itsAge = age;} private: int itsAge; }; SimpleCat::SimpleCat() { std::cout << "SimpleCat Constructor Performed ..." << std::endl; itsAge = 1; } SimpleCat::SimpleCat(SimpleCat&) { std::cout << "SimpleCat Copy Constructor Performed ..." << std::endl; } SimpleCat::~SimpleCat() { std::cout << "SimpleCat Destructor Performed ..." << std::endl; } const SimpleCat & functionTwo(const SimpleCat & srcCat); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); std::cout << "Making a cat ..." << std::endl; SimpleCat Frisky; std::cout << "Frisky is " << Frisky.getAge() << "years old" << std::endl; int age = 5; Frisky.setAge(age); std::cout << "Frisky is " << Frisky.getAge() << "years old" << std::endl; std::cout << "Calling FunctionTwo ..." << std::endl; functionTwo(Frisky); std::cout << "Frisky is " << Frisky.getAge() << "years old" << std::endl; return 0; return a.exec(); } //函數進來指向常對象的引用,函數返回指向常對象的引用 //FUC2 takes and returns a reference to a constant object const SimpleCat & functionTwo(const SimpleCat &srcCat) { std::cout << "Function Two. Returning..." << std::endl; std::cout << "Frisky is now" << srcCat.getAge() << "years old" << std::endl; //srcCat.setAge(8); return srcCat; }
註意高亮部分,主次被註釋掉的語句,如果takes a reference to a constant object,就不可以修改了,這樣就達到了安全的目的,至於函數2最前面的那個const我就不知道啥意思了
什麽時候用引用,什麽時候用指針?
引用用起來方便,能用就盡量用
引用只能被賦值一次,所有如果要多次賦值,就用指針。引用不可以為NULL,所有如果有不確定對象,用指針
不可以返回對局部變量的引用
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Man &rMan = theFunction(); std::cout << "rMan is " << rMan.getAge() << " years old!" << std::endl; return 0; return a.exec(); } Man &theFunction() { Man bob(5,9); return bob; }
編譯也許會通過,但是一定會系統崩潰。
返回對heap上對象的引用
Man &theFunction(); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Man &rMan = theFunction(); std::cout << &rMan << std::endl; std::cout << "rMan is " << rMan.getAge() << " years old!" << std::endl; return 0; return a.exec(); } Man &theFunction() { Man *pBob = new Man(5,9); std::cout << pBob << std::endl; return *pBob; }
執行結果,可以看到,即使在theFunction域外,堆上的這個對象存活了下來,函數返回後還可以使用。
這節課太難了……
HOUR 13 Developing Advanced References and Pointer