const使用注意事項 指針 引用,指針引用區別

整理了const 指針 應用,,

const用法:

char greeting[] = "Hellow";
const char* p = greeting;//non-const pointer,const data
char const * p = greeting;//同上


char* const p = greeting;//non-const data,const pointer
const char* const p = greeting;//const data,const pointer


const出現在星號左邊,表示被指物是常量;出現在指針右邊,表示指針自身是常量;若兩邊都有,表示被指物和指針都是常量。

const優點:

(1) constdefine兩者都可以用來定義常量。Const定義常量類型,可進行類型檢查而後者define只進行字符替換,沒有類型安全檢查,並且在字符替換時可能產生意想不到的錯誤(邊際錯誤)。有些集成化的調試工具可以對const常量進行調試,但是不能對宏常量進行調試。

Const float PI = 3.1415926; #define PI 3.1415926

(2)宏是字符常量,在編譯階段完成所有宏替換,同時宏消失;const同樣在編譯階段發生全部替換,但是,常量名不會消失,編譯器會把它放進符號表中。

(3)防止意外修改,增強程序的健壯性。Void Fun(const int i){ i=1;//錯誤}

(4)Const節省了空間,避免了不必要的內存分配,同時提高了效率。編譯器通常不爲const只讀變量分配存儲空間,而是將他保存在符號表中,使其成爲一個編譯期間的值,沒有了存儲與讀內存的操作。const定義常量從彙編的角度來看,只是給出了對應的內存地址,而不是象#define一樣給出的是立即數,所以,const定義的常量在程序運行過程中只有一份拷貝,而#define定義的常量在內存中有若干個拷貝。

#define PI 3.14159         //常量宏
const doulbe Pi=3.14159; //此時並未將Pi放入ROM中
              ......
double i=Pi;   //此時爲Pi分配內存,以後不再分配!
double I=PI; //編譯期間進行宏替換,分配內存
double j=Pi; //沒有內存分配
double J=PI; //再進行宏替換,又一次分配內存!

Const修飾函數的參數:

如果函數做輸出用,不論他是什麼數據類型,也不論採用指針傳遞還是引用傳遞,都不能加const修飾,否則該參數將失去輸出功能。

 

Const只能修飾輸入參數:

輸入參數爲指針:

如果輸入參數採用“指針傳遞”,那麼加 const 修飾可以防止意外地改動該指針,起到保護作用。

例如 StringCopy 函數: void StringCopy(char *strDestination, const char *strSource);

其中 strSource 是輸入參數,strDestination是輸出參數。給 strSource加上 const修飾後,如果函數體內的語句試圖改動strSource 的內容,編譯器將指出錯誤。

 

輸入參數值傳遞:

如果輸入參數採用“值傳遞”,由於函數將自動產生臨時變量用於複製該參數,該輸入參數本來就無需保護,所以不要加 const 修飾。

例如不要將函數 void Func1(int x) 寫成void Func1(const int x)。同理不要將函數void Func2(A a) 寫成void Func2(const A a)。其中A 爲用戶自定義的數據類型。

 

非內置類型const引用:

對於非內部數據類型的參數而言,象 void Func(A a) 這樣聲明的函數註定效率比較底。因爲函數體內將產生 A 類型的臨時對象用於複製參數 a,而臨時對象的構造、

複製、析構過程都將消耗時間。爲了提高效率,可以將函數聲明改爲 void Func(A &a),因爲“引用傳遞”僅借用一下參數的別名而已,不需要產生臨時對象。但是函數void Func(A &a) 存在一個缺點:“引用傳遞”有可能改變參數a,這是我們不期望的。解決這個問題很容易,加const修飾即可,因此函數最終成爲 void Func(const A &a)以此類推,是否應將 void Func(int x) 改寫爲 void Func(const int &x),以便提高效率?完全沒有必要,因爲內部數據類型的參數不存在構造、析構的過程,而複製也非常快,“值傳遞”和“引用傳遞”的效率幾乎相當。

 

對於非內部數據類型的輸入參數,應該將“值傳遞”的方式改爲“const引用傳遞”,目的是提高效率。例如將 void Func(A a) 改爲 void Func(const A &a)

 

內置類型參數const引用:

對於內部數據類型的輸入參數,不要將“值傳遞”的方式改爲“const 引用傳遞”。否則既達不到提高效率的目的,又降低了函數的可理解性。例如 void Func(int x) 應該改爲 void Func(const int &x)

 

用 const 修飾函數的返回值:

指針返回值:

如果給以“指針傳遞”方式的函數返回值加 const 修飾,那麼函數返回值(即指針)的內容不能被修改,該返回值只能被賦給加const 修飾的同類型指針。

例如函數

   const char * GetString(void);

如下語句將出現編譯錯誤:

   char *str = GetString();

正確的用法是

   const char *str = GetString();

 

引用返回值:

函數返回值採用“引用傳遞”的場合並不多,這種方式一般只出現在類的賦值函數中,目的是爲了實現鏈式表達。

例如

  class A

  {…

   A & operate = (const A &other);  // 賦值函數

  };

  A a, b, c;    // a, b, c A的對象

  …

  a = b = c;    // 正常的鏈式賦值


const 成員函數

  任何不會修改數據成員的函數都應該聲明爲 const 類型。如果在編寫const 成員函數時,不慎修改了數據成員,或者調用了其它非const 成員函數,編譯器將指出錯誤,

這無疑會提高程序的健壯性。


以下程序中,類 stack 的成員函數GetCount 僅用於計數,從邏輯上講GetCount 當爲const 函數。編譯器將指出GetCount 函數中的錯誤。

  class Stack

{

   public:

   void   Push(int elem);

   int   Pop(void);

   int   GetCount(void)  const; // const 成員函數

   private:

   int   m_num;

   int   m_data[100];

};

 

  int Stack::GetCount(void)  const

{

   ++ m_num;  // 編譯錯誤,企圖修改數據成員 m_num

  Pop();   // 編譯錯誤,企圖調用非 const函數

  return m_num;

  }


mutable解決const  成員函數不能被改變的情況。

指針和引用的區別(網上總結):

(1)引用總是指向一個對象,沒有所謂的空引用。因爲沒有空引用,因此在使用時無需測試是否有值,而使用指針則需要測試其有效性。不存在指向空值的引用這個事實意味着使用引用的代碼效率比使用指針的要高。因爲在使用引用之前不需要測試它的合法性,其次指針產生地址的copy。

void printDouble(const double& rd) 
{ 
cout << rd; // 不需要測試rd,它 
} // 肯定指向一個double值 
//相反,指針則應該總是被測試,防止其爲空: 
void printDouble(const double *pd) 
{ 
if (pd) { // 檢查是否爲NULL 
cout << *pd; 
} 
} 

(2)指針可以被重新賦值,而引用總是指向最初的值。

string s1("Nancy"); 
string s2("Clancy"); 
string& rs = s1; // rs 引用 s1 
string *ps = &s1; // ps 指向 s1 
rs = s2; // rs 仍舊引用s1, 
// 但是 s1的值現在是 
// "Clancy" 
ps = &s2; // ps 現在指向 s2; 
// s1 沒有改變 

 (3)必須使用引用的場合:operator[]操作符,由於操作符必須返回能夠被當做賦值對象的東西,所以需要返回一個引用。例如:str = str 1 = str2;因此必須返回對象本身。


(4)sizeof引用得到是指向對象的大小,sizeof指針是指針本身的大小。

(5)在任何情況下都不能用指向空值的引用。一個引用必須總是指向某些對象。因此如果你使用一個變量並讓它指向一個對象,但是該變量在某些時候也可能不指向任何對象,這時你應該把變量聲明爲指針,因爲這樣你可以賦空值給該變量。相反,如果變量肯定指向一個對象,例如你的設計不允許變量爲空,這時你就可以把變量聲明爲引用。

char *pc = 0; // 設置指針爲空值 
char& rc = *pc; // 讓引用指向空值 

這是非常有害的,毫無疑問。結果將是不確定的(編譯器能產生一些輸出,導致任何事情都有可能發生),應該躲開寫出這樣代碼的人除非他們同意改正錯誤。如果你擔心這樣的代碼會出現在你的軟件裏,那麼你最好完全避免使用引用,要不然就去讓更優秀的程序員去做。

(6)總的來說,在以下情況下你應該使用指針,一是你考慮到存在不指向任何對象的可能(在這種情況下,你能夠設置指針爲空),二是你需要能夠在不同的時刻指向不同的對象(在這種情況下,你能改變指針的指向)。如果總是指向一個對象並且一旦指向一個對象後就不會改變指向,那麼你應該使用引用。


如果函數採用引用方式傳遞,使用指針和引用作爲參數類型都是可以的。使用指針和引用類型作爲函數參數各有優缺點,視具體環境而定。對於引用類型,引用必須被初始化爲一個對象,並且不能在使她指向其他對象,因爲對引用賦值實質上是對目標對象的賦值。這是引用的缺點。優點,上邊提到的也是引用的優點,因爲在函數調用時需要驗證引用參數的合法性。使用指針作爲函數參數,用戶可以隨便修改指針指向的對象。其他區別就是用法的區別了,引用只需要傳值,指針需要傳地址。

void test(int& w)
{
	int a = 1;


	w = a;
}


void test1(int* w)
{
	int a = 2;


	w = &a;
}


void main()
{
	int p = 0;


	test(p);
	test1(&p);
 }

test通過引用傳參,改變函數值。引用傳參就是傳的當前對象實際地址,因此通過引用傳遞實參其實就是傳遞的本身。所以通過引用進行參數傳遞不僅節約時間而且還節約空間。
但是當通過指針進行傳參時,也是將實參的一個拷貝傳遞給形參,因此上例中對形參w的改變不會影響主函數p的改變。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章