c++的引用一直都是一個(gè)很重要的概念,而且使用的場合很多,比如通過引用減少不必要的臨時(shí)對象拷貝,或者通過傳遞引用作為函數(shù)的返回值。
int get_stm(int& stm); 這里的stm就是需要get的值,而函數(shù)本身的返回值則作為錯(cuò)誤判斷的依據(jù),例如返回0則正確,非0值則表示錯(cuò)誤等。
前面說的這些都是很常規(guī)的用法,也不存在什么誤區(qū)。最近在設(shè)計(jì)一個(gè)內(nèi)存模型時(shí)有這樣的一個(gè)需求:
class Table
{
int table_id;
Row* row_array;
};
class Row
{
int table_id;
int column_num;
...
...
}
在這兩個(gè)類中,第一個(gè)類描述一個(gè)數(shù)據(jù)庫的表,而第二個(gè)類則描述了一個(gè)表中的某一行的信息,這里面存在著數(shù)據(jù)的冗余,table_id。因?yàn)橐粡埍淼拿恳恍械膖able_id都是相同的,而如果想要在每一行中省略掉table_id的內(nèi)存開銷,有幾種方式。
1 將table_id設(shè)定為static
2 將table_id改為一個(gè)引用
先說第一種方式,看似可以解決問題,但實(shí)際中,由于涉及到多張表,每張表的table_id必定不同,而如果將Row的table_id設(shè)定為static,就會(huì)出現(xiàn)每張表的每行數(shù)據(jù)的table_id是相同的,這樣顯然是錯(cuò)誤的。如果改為一個(gè)static數(shù)組,似乎可以解決問題,但又過于復(fù)雜。
第二種方式,利用引用,代碼大致是這樣的:
class Row
{
const int& table_id;
...
public:
Row(const int& t_id) : table_id(t_id)
{}
};
看上去很完美,既節(jié)省空間,而且關(guān)系清晰,不會(huì)出現(xiàn)問題。
在這種方式中,我們做了一個(gè)假定:c++中的引用不會(huì)占用額外的內(nèi)存。
從引用的原理上看確實(shí)是這樣,它是一個(gè)別名,作為一個(gè)并不存在的東西指向?qū)嶋H的變量...
但實(shí)際上呢?
如果你在gcc中測試這樣的一段代碼:
class A
{
int& a;
int& b;
int& c;
int& d;
int e;
};
int main()
{
cout << sizeof(A) << endl;
return 0;
}
就會(huì)發(fā)現(xiàn)輸出的結(jié)果是36 !!!! (64位環(huán)境)
36 = 32 + 4; 也就是4個(gè)指針和一個(gè)int。
換句話說,引用并不像它看上去的那么美好,其實(shí)不過是一層指針的封裝.....
一直以來都覺得引用是一種類似作用域的擴(kuò)展,也就是把變量原本的作用域,擴(kuò)展到了需要的地方,而實(shí)現(xiàn)的方式,大致是在鏈接的時(shí)候,將所引用的符號變量替換為實(shí)際的地址.....雖然也聽過引用的實(shí)現(xiàn)和指針類似,但是還一直覺得是原理類似,而不是實(shí)現(xiàn)。
但現(xiàn)實(shí)真的很殘酷。
在gcc,clang,vc等等編譯器中,都是使用一個(gè)簡單的指針替換引用,大概類似這樣:
int b;
int& c = b;
c = 3;
//實(shí)際實(shí)現(xiàn)時(shí)
int b;
int* c = &b;
*c = 3;
注:就算是全局空間的引用,也需要占用一個(gè)指針的內(nèi)存,編譯器并沒有對其做任何優(yōu)化。
深究其原因,是因?yàn)閏++標(biāo)準(zhǔn)中并沒有定義引用該如何實(shí)現(xiàn),而大家就不約而同的選擇了相對簡單的實(shí)現(xiàn)方式....
結(jié)論:
1 引用并不會(huì)節(jié)省內(nèi)存,開銷相當(dāng)于一個(gè)指針
2 所有使用引用的地方都可以用指針替代,反之則不然(無法實(shí)現(xiàn)空引用)
(原文時(shí)間2014-1-21)