C++/C++11中引用的使用
引用(reference)是一種復合類型(compound type)。引用為對象起了另外一個名字,引用類型引用(refer to)另外一種類型。通過將聲明符寫成&d的形式來定義引用類型,其中d是聲明的變量名。
一、一般引用:
??????? 一般在初始化變量時,初始值會被拷貝到新建的對象中。然而定義引用時,程序把引用和它的初始值綁定(bind)在一起,而不是將初始值拷貝給引用。一旦初始化完成,引用將和它的初始值對象一直綁定在一起。因為無法令引用重寫綁定到另外一個對象,因此引用必須初始化。
??????? 為引用賦值,實際上是把值賦給了與引用綁定的對象。獲取引用的值,實際上是獲取了與引用綁定的對象的值。同理,以引用作為初始值,實際上是以與引用綁定的對象作為初始值。
引用并非對象,相反的,它只是為一個已經存在的對象所起的另外一個名字,引用即別名。定義了一個引用之后,對其進行的所有操作都是在與之綁定的對象上進行的。
因為引用本身不是一個對象,所以不能定義引用的引用。
允許在一條語句中定義多個引用,其中每個引用標識符都必須以符號&開頭。
引用只能綁定到對象上,而不能與字面值或某個表達式的計算結果綁定在一起。
二、const的引用:
??????? 可以把引用綁定到const對象上,就像綁定到其它對象上一樣,稱之為對常量的引用(reference to const)。與普通引用不同的是,對常量的引用不能被用作修改它所綁定的對象。
??????? 常量的引用僅對引用可參與的操作做出了限定,對于引用的對象本身是不是一個常量未作限定。因為對象也可能是個非常量,所以允許通過其它途徑改變它的值。
三、傳引用參數:
通過使用引用形參,允許函數改變一個或多個實參的值。
使用引用避免拷貝:拷貝大的類類型對象或者容器對象比較低效,甚至有的類類型(包括IO類型在內)根本就不支持拷貝操作。當某種類型不支持拷貝操作時,函數只能通過引用形參訪問該類型的對象。
使用引用形參返回額外信息:一個函數只能返回一個值,然而有時函數需要同時返回多個值,引用形參為一次返回多個結果提供了有效的途徑。
函數參數是否需要傳引用的判別:(1)、需要在函數內部改變實參的值;(2)、函數傳入值較大,為避免拷貝的代價,使用引用。
在函數調用時在內存中不會生成副本,用引用傳遞函數的參數,能保證參數傳遞中不產生副本,提高傳遞的效率,且通過const的使用,保證了引用傳遞的安全性。
Advantages of passing by reference:
(1)、It allows a function to change the value of the argument, which is sometimes useful.Otherwise, const references can be used to guarantee the function won’t change the argument.
(2)、Because a copy of the argument is not made, it is fast, even when used with large structs or classes.
(3)、References can be used to return multiple values from a function.
(4)、References must be initialized, so there’s no worry about null values.
Disadvantages of passing by reference:
(1)、Because a non-const reference cannot be made to an rvalue (e.g. a literal or an expression), reference arguments must be normal variables.
(2)、It can be hard to tell whether a parameter passed by non-const reference is meant to be input, output, or both. Judicious use of const and a naming suffix for out variables can help.
(3)、It’s impossible to tell from the function call whether the argument may change. An argument passed by value and passed by reference looks the same. We can only tell whether an argument is passed by value or reference by looking at the function declaration. This can lead to situations where the programmer does not realize a function will change the value of the argument.
When to use pass by reference:
(1)、When passing structs or classes (use const if read-only).
(2)、When you need the function to modify an argument.
When not to use pass by reference:
(1)、When passing fundamental types (use pass by value).
四、右值引用:
為了支持移動操作,C++11引入了一種新的引用類型----右值引用(rvalue reference)。所謂右值引用就是必須綁定到右值的引用。通過&&而不是&來獲得右值引用。右值引用有一個重要的性質----只能綁定到一個將要銷毀的對象。因此,可以自由地將一個右值引用的資源”移動”到另一個對象中。
一般而言,一個左值表達式表示的是一個對象的身份,而一個右值表達式表示的是對象的值。
類似任何引用,一個右值引用也不過是某個對象的另一個名字而已。對于常規引用(為了與右值引用區分開來,可以稱之為左值引用(lvalue reference)),不能將其綁定到要求轉換的表達式、字面常量或是返回右值的表達式。右值引用有著完全相反的綁定特性:可以將一個右值引用綁定到這類表達式上,但不能將一個右值引用直接綁定到一個左值上。
返回左值引用的函數,連同賦值、下標、解引用和前置遞增/遞減運算符,都是返回左值得表達式的例子。可以將一個左值引用綁定到這類表達式的結果上。
返回非引用類型的函數,連同算術、關系、位以及后置遞增/遞減運算符,都生成右值。不能將一個左值引用綁定到這類表達式上,但可以將一個const的左值引用或者一個右值引用綁定到這類表達式上。
左值持久,右值短暫:左值有持久的狀態,而右值要么是字面常量,要么是在表達式求值過程中創建的臨時對象。
由于右值引用只能綁定到臨時對象,可知:所引用的對象將要被銷毀;該對象沒有其它用戶。這兩個特性意味著,使用右值引用的代碼可以自由地接管所引用的對象的資源。
變量是左值:變量可以看作只有一個運算對象而沒有運算符的表達式。
變量是左值,因此不能將一個右值引用直接綁定到一個變量上,即使這個變量是右值引用類型也不行。
雖然不能就將一個右值引用直接綁定到一個左值上,但可以顯示地將一個左值轉換為對應的右值引用類型。還可以通過調用一個名為move的新標準庫函數來獲得綁定到左值上的右值引用,此函數定義在頭文件utility中。move調用告訴編譯器:有一個左值,但希望像一個右值一樣處理它。在調用move之后,我們不能對移后源對象的值做任何假設。
我們可以銷毀一個移后源對象,也可以賦予它新值,但不能使用一個移后源對象的值。
關于C++11中右值引用的更多介紹可以參數:http://blog.csdn.net/fengbingchun/article/details/52562004?
五、引用作為函數返回值
以引用返回函數值,定義函數時需要在函數名前加&。用引用返回一個函數值得最大好處是,在內存中不產生被返回值的副本。
引用作為返回值,必須遵守以下規則:
(1)、不能返回局部變量的引用。
(2)、不能返回函數內部new分配的內存的引用。
(3)、可以返回類成員的引用,但最好是const。
當函數返回值類型為引用時,一般就用引用類型去接收,如果用非引用類型接收,就等于將函數返回的引用的數據值,復制給了該接收對象,和函數返回非引用類型是一樣的效果。
不可以將常引用當作函數返回值返回。
能用常引用的地方盡量用常引用。
以下是測試代碼:
#include "reference.hpp"
#include <iostream>
#include <utility> // std::moveint test_reference_1()
{// 1. 普通的引用int ival = 1024;int &refVal = ival; // refVal指向ival(是ival的另一個名字)//int &refVal2; // error:引用必須被初始化int &refVal3 = refVal; // refVal3綁定到了那個與refVal綁定的對象上,這里就是綁定到ival上// 利用與refVal綁定的對象的值初始化變量iint i = refVal; // correct:i被初始化為ival的值// 允許在一條語句中定義多個引用,其中每個引用標識符都必須以符號&開頭int i1 = 1024, i2 = 2048;int &r = i1, r2 = i2;int i3 = 1024, &ri = i3;int &r3 = i3, &r4 = i2; // r3和r4都是引用//int &refVal4 = 10; // error: 引用類型的初始值必須是一個對象double dval = 3.14;//int &refVal5 = dval; // error: 此處引用類型的初始值必須是int型對象const int &refVal6 = dval; // correct// 編譯器把上述語句變成了如下形式://const int temp = dval; // 由雙精度浮點數生成一個臨時的整型常量//const int &refVal6 = temp; // 讓refVal6綁定這個臨時量// 2. const的引用// 對常量的引用不能被用作修改它所綁定的對象const int ci = 1024;const int &r1 = ci; // correct: 引用及其對應的對象都是常量//r1 = 42; // error: r1是對常量的引用//int &r2 = ci; // error: 試圖讓一個非常量引用指向一個常量對象int i4 = 42;const int &r6 = i4; // 允許將const int&綁定到一個普通int對象上const int &r7 = 42; // correct: r7是一個常量引用const int &r8 = r6 * 2; // correct: r8是一個常量引用//int &r9 = r6 * 2; // error: r9是一個普通的非常量引用// 常量的引用僅對引用可參與的操作做出了限定,對于引用的對象本身是不是一個常量未作限定int x = 42;int &rx = x; // 引用rx綁定對象xconst int &ry = x; // ry也綁定對象x,但是不允許通過ry修改x的值rx = 0; // rx并非常量,x的值修改為0//ry = 0; // error: ry是一個常量引用return 0;
}// 該函數接受一個int對象的引用,然后將對象的值置為0
static void reset(int &i) // i是傳給reset函數的對象的另一個名字
{i = 0; // 改變了i所引用對象的值
}int test_reference_2()
{int j = 42;reset(j); // j采用引用方式,它的值被改變fprintf(stderr, "j = %d\n", j); // j = 0return 0;
}int test_reference_3()
{int i = 42;int &r = i; // correct: r引用i//int &&rr = i; // error: 不能將一個右值引用綁定到一個左值上//int &r2 = i * 42; // error: i*42是一個右值const int &r3 = i * 42; // correct: 可以將一個const的引用綁定到一個右值上int &&rr2 = i * 42; // correct: 將rr2綁定到乘法結果上int &&rr1 = 42; // correct: 字面常量是右值//int &&rr3 = rr1; // error: 表達式rr1是左值int &&rr4 = std::move(rr1); // correctreturn 0;
}
GitHub: https://github.com/fengbingchun/Messy_Test
總結
以上是生活随笔為你收集整理的C++/C++11中引用的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用Caffe进行手写数字识别执行流程解
- 下一篇: OpenFace库(Tadas Balt