C與C++的區別(3)——指針與引用

我們在學習c語言的時候,知道函數傳參有兩種,值傳遞和地址傳遞;而在C++中,多了一個引用的概念,引用就是給已存在的變量取了一個別名,編譯器不會給引用開闢新的空間,與其引用的變量共用一塊空間。

 

引用於指針的區別

引用:引用就是對某一變量的一個別名,對引用的操作對變量直接操作完全一致;

int a = 10;
int *p = a;
  • &在此不是求地址運算,而是起到標識作用。
  • 聲明引用時,必須初始化。
  • 聲明引用後,不能改變該應用的值。
  • 聲明一個引用,不是定義了一個變量,它只表示該引用名是目標變量的一個別名,編譯器不會爲引用分配內存空間。
  • 無法引用一個數組,因爲數組是若干個元素的集合,無法建立一個數組的別名。

指針:指向一塊內存,可以通過解引用改變內存塊的值。

int a;
int &ra =a;
  •  指針時一個實體,而引用是一個別名
  • 引用被初始化後不可改變,而指針可以改變
  • 指針在使用時需要解引用,而引用不需要解引用
  • 指針可爲空,而引用不可以爲空
  • sizeof 引用,得到的是所指變量(對象)的大小,而sizeof 指針,得到指針本身的大小
  • 指針可以指向一個數組,而引用不可以建立一個數組的別名。

 

引用的應用

(1)引用作爲參數

在c中傳參有兩種形式:值傳遞和地址傳遞;我們知道在函數調用使用值傳遞時,要將參數壓棧(有關函數調用壓棧詳見:https://blog.csdn.net/QX_a11/article/details/83959578),如果是大塊數據作爲參數傳遞的時候,採用指針的方式,這樣可以避免大塊內存全部壓棧;在C++中引用指針後,被調函數的形參就成爲原來主調函數中的實參變量或對象的一個別名來使用,所以在被調函數中對形參變量的操作就是直接對其相應的目標對象(主調函數)的操作。使用引用並沒有在內存中產生副本。從而提高了效率。例如:

void swap(int &a,int &b)
{
	int temp = a;
	a = b;
	b = temp;
}
int main()
{
	int a = 10;
	int b = 20;
	swap(a,b);
	cout << a << b <<endl; 
}

如果既要利用引用提高程序的效率,又要保護傳給函數的變量不被修改,這就要使用到常引用。

(2)常引用

要想保證目標變量不被修改則需加const,達到了引用的安全性;

引用聲明:const 類型標識符 &引用名 = 目標變量名

int a = 10;
const int &ra = a;
//ra = 20;error
a = 20;

常引用也有其他方面的需求:

string foo();
void bar(string &s);

bar(foo());//非法
bar("Hello");//非法

在如上代碼的foo()和“Hello”都會生成臨時變量;原因是類型不匹配發生類型強轉,進而生成臨時變量;臨時變量都是const修飾的常量。因此在調用點嘗試將const類型轉化爲非const類型,這樣是非法的。所以引用型參數儘量聲明爲const類型。

(3)引用作爲返回值

格式:類型標識符 &函數名(形參列表)

用引用返回一個函數值的最大好處是,在內存中不產生返回值的副本

#include <iostream>
float temp; //定義全局變量temp
float fn1(float r); //聲明函數fn1
float &fn2(float r); //聲明函數fn2
float fn1(float r) //定義函數fn1,它以返回值的方法返回函數值
{
	temp = (float)(r*r*3.14);
	return temp;
}
float &fn2(float r) //定義函數fn2,它以引用方式返回函數值
{
	temp=(float)(r*r*3.14);
	return temp;
}
void main() //主函數
{
	float a=fn1(10.0); //第1種情況,系統生成要返回值的副本(即臨時變量)
	/*float &b= fn1(10.0);*/ //第2種情況,可能會出錯(不同 C++系統有不同規定)
	//不能從被調函數中返回一個臨時變量或局部變量的引用
	float c=fn2(10.0); //第3種情況,系統不生成返回值的副本
	//可以從被調函數中返回一個全局變量的引用
	float &d=fn2(10.0); //第4種情況,系統不生成返回值的副本
	//可以從被調函數中返回一個全局變量的引用
	std::cout<<a<<c<<d;
} 

引用作爲返回值,必須遵守以下規則:

  • 不能返回局部變量或臨時變量的引用;局部變量會在函數返回後被銷燬,引用沒有實際的意義。
  • 不能返回函數內部new分配的內存的引用:雖然不存在局部變量的被動銷燬問題,可對於這種情況(返回函數內部new分配內存的引用),又面臨其他尷尬的局面。例如,被函數返回的引用只是個臨時變量,那麼這個引用所指向的空間就無法釋放
  • 引用於一些操作符的重載:流操作符<<和>>,這兩個操作符都希望可以被連續使用,比如cout << a<<b<<endl;可供選擇的返回值可以是流對象也可以是指向流對象的指針,如果返回流對象,則每次返歸都會調用拷貝構造函數生成新的對象,顯然是不可取的;那返回流對象的指針呢,返回流對象的指針又不可以連續使用;所以返回流對象的引用是重載流操作符的唯一選擇。賦值運算符和流運算符一樣,都希望可以被連續使用,a = b = 10;賦值操作符的返回值必須是一個左值,以便可以繼續賦值。

 

引用的總結

1、引用是給某個變量取個別名,主要目的是在函數傳遞參數中,解決大內存塊的傳遞效率和內存的使用情況;在傳遞中不產生副本,並且通過const的使用,保證了引用傳遞的安全性。

2、引用與指針的區別是,指針通過某個指針變量指向一個對象後,對它所指向的變量間接操作。而引用本身就是目標變量的別名,對引用的操作就是對目標變量的操作。

3、引用使用在對流操作符<<和>>、賦值操作符=的返回值、拷貝構造函數的參數、賦值操作符=的參數等

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章