1. 程式人生 > >HOUR 13 Developing Advanced References and Pointer

HOUR 13 Developing Advanced References and Pointer

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>

class
SimpleCat { 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:

  1. main函數傳值給函數1,相當於復制一個object給被調函數,所以復制構造函數被執行->SimpleCat Copy Constructor Performed ... 這裏srcCat的存儲位置在stack
  2. 函數1執行-> function one returning
  3. 函數1返回object給主函數(傳值復制方式),再一次調用復制構造函數,把處理後的object復制給主函數。
  4. 第三步裏子函數向主函數復制object時,產生臨時對象(tempObject 我們看不見),在返回結束後被銷毀,所以執行了一次constructor和destructor
  5. 函數1返回後,該函數結束,srcCat 生命周期結束,調用destructor

函數2:

  1. 主函數向子函數傳遞參數使用reference,未調用constructor
  2. 子函數向主函數返回的也是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