1. 程式人生 > >C++基礎---有返回值型別函式(返回引用型別)

C++基礎---有返回值型別函式(返回引用型別)

1. C++基礎—有返回值型別函式(返回引用型別)

1.1 引用的定義

  • 引用就是某個目標變數的“別名”(alias),對引用的操作與對變數直接操作效果完全相同。
  • 申明一個引用的時候,切記要對其進行初始化。引用宣告完畢後,相當於目標變數名有兩個名稱,即該目標原名稱和引用名,不可以把該引用名作為其他變數名的別名。
  • 程式碼示例:

    (1)會呼叫拷貝建構函式和解構函式
    A a(){...;return *this;}
    
    (2)不會呼叫拷貝建構函式和解構函式
    A& a(){...;return *this;}
    
    當返回一個變數時,會產生拷貝。當返回一個引用時,不會發生拷貝,你可以將引用看作是一個變數的別名,就是其他的名字,引用和被引用的變數其實是一個東西,只是有了兩個名字而已。

    注:再次執行相關操作只能是賦值,而不是改改變引用的物件)。宣告一個引用,不是新定義了一個變數,它只表示該引用名是目標變數名的一個別名,它本身不是一種資料型別,因此引用本身不佔儲存單元,系統也不給引用分配儲存單元。

1.2 引用作為引數傳遞迴顧

  • 傳遞引用給函式與傳遞指標的效果是一樣的。這時,被調函式的形參就成為原來主調函式中的實參變數或物件的一個別名來使用,所以在被調函式中對形參變數的操作就是對其相應的目標物件(在主調函式中)的操作。
  • 使用引用傳遞函式的引數,在記憶體中並沒有產生實參的副本,它是直接對實參操作;而使用一般變數傳遞函式的引數,當發生函式呼叫時,需要給形參分配儲存單元,形參變數是實參變數的副本;如果傳遞的是物件,還將呼叫拷貝建構函式。因此,當引數傳遞的資料較大時,用引用比用一般變數傳遞引數的效率和所佔空間都好。
  • 使用指標作為函式的引數雖然也能達到與使用引用的效果,但是,在被調函式中同樣要給形參分配儲存單元,且需要重複使用”*指標變數名”的形式進行運算,這很容易產生錯誤且程式的閱讀性較差;另一方面,在主調函式的呼叫點處,必須用變數的地址作為實參。而引用更容易使用,更清晰

1.3 將引用作為函式返回值型別

  • 格式:型別識別符號& 函式名(形參列表及型別說明){ //函式體 }
  • 好處:在記憶體中不產生被返回值的副本;
  • 示例程式碼一:

    
    #include <iostream>
    
    using namespace std;
    
    class A
    {
    public:
        A(){}
        ~A(){}
    
    public
    : void set_x(int x){_x=x;} int& get_x(){return _x;} private: int _x; }; int main() { A *a = new A(); a->set_x(14); int x = a->get_x();//na為新定義的變數,初始化為a->get_a()的值 x = 24; cout<<"x="<<x<<", _x="<<a->get_x()<<endl; int &rx = a->get_x();//na1為引用,是a->get_a()返回變數的別名,初始化為a->get_a()的值 rx = 24; cout<<"rx="<<rx<<", _x="<<a->get_x()<<endl; delete a; system("pause"); return 0; } =>x=24, _x=14 x=24, _x=24
  • 示例程式碼二:

    
    #include <iostream>
    
    
    #include <string>
    
    
    #include <map>
    
    using namespace std;
    
    typedef map<int, string> STR_MAP;
    class A
    {
    public:
        A(){_str_map.clear();}
        ~A(){}
    
    public:
        STR_MAP& get_str_map()
        {
            return _str_map;
        }
    
        void insert_to_str_map(int index, string name)
        {
            _str_map.insert(pair<int,string>(index, name));
        }
    
        void show_str_map()
        {
            STR_MAP::iterator iter = _str_map.begin();
            for(; iter!=_str_map.end(); ++iter)
            {
                cout<<"index="<<iter->first<<", name="<<iter->second<<endl;
            }
        }
    
    private:
        STR_MAP _str_map;
    };
    
    int main() 
    { 
        A *a = new A();
        int i=0;
        for(; i<3; i++)
        {
            char cstr[16];
            string name(itoa(i, cstr, 16));
            a->insert_to_str_map(i, "a" + name);
        }
    
        STR_MAP str_map = a->get_str_map();
        str_map.insert(pair<int,string>(i, "b"));
        a->show_str_map();
    
        cout<<endl;
    
        STR_MAP &rstr_map = a->get_str_map();
        rstr_map.insert(pair<int,string>(i, "b"));
        a->show_str_map();
    
        delete a;
    
        system("pause");
    
        return 0;
    }
    =>index=0, name=a0
      index=1, name=a1
      index=2, name=a2
    
      index=0, name=a0
      index=1, name=a1
      index=2, name=a2
      index=3, name=b

    注:正是因為這點原因,所以返回一個區域性變數的引用是不可取的。因為隨著該區域性變數生存期的結束,相應的引用也會失效,產生runtime error!

1.4 返回區域性變數的引用

  • 返回區域性內建型別的引用:

    
    #include <iostream>
    
    using namespace std;
    
    int& func()
    {
        int a;
        a = 10;
        return a;
    }
    
    int main() 
    { 
        int &ra = func();
        ra += 1;
        cout<<"a="<<func()<<", ra="<<ra<<endl;
        ra += 1;//變數a是區域性變數,生命週期在執行函式右大括號時結束,其別名也相應結束
        cout<<"a="<<func()<<", ra="<<ra<<endl;
        system("pause");
        return 0;
    }
    =>a=10, ra=11
      a=10, ra=1448013121
    程式並未崩潰,只是輸出未如預期。
  • 返回區域性物件的引用:

    
    #include <iostream>
    
    
    #include <string>
    
    using namespace std;
    
    string& func()
    {
        string a;
        a = 10;
        return a;
    }
    
    int main() 
    { 
        string a = func();
        cout<<"a="<<a<<endl;
    
        string &ra = func();
        ra = "1";
        cout<<"a="<<func()<<", ra="<<ra<<endl;
    
        system("pause");
        return 0;
    }
    =>編譯通過,執行報錯
    提示:123.exe 中的 0x75644598 處有未經處理的異常: Microsoft C++ 異常: 記憶體位置 0x00b5ebe8 處的 std::bad_alloc。

    注:問題的關鍵是,當想要返回一個引用而不是一個拷貝時,務必要確保這個引用的有效性。比如:
    int& fun() { int a; a=10; return a; }
    或 string& fun() { string a; a=”10”; return a; }
    這樣是不行的,因為a會在fun退出時被銷燬,這時返回的a的引用是無效的。這種情況下,如果fun的返回型別不是int&而是int(或string&是string)就沒有問題了。
    因此,要返回一個引用時,“臨時變數”不能是“臨時”的,至少得等函式外部使用完畢這個引用之後,才能銷燬它。

參考文獻:
[1]《C++全方位學習》範磊——第十三章
[2]《C++程式設計教程(第二版)》錢能——第五章、第六章、第七章
[3]《C++ Primer(第5版)》王剛 楊巨峰——第一章、第六章
[4] 百度搜索關鍵字:C++函式、返回引用型別