C++ 中常用關鍵字及其用法


0.1 C++與C的對比

  1. C++有三種編程方式:過程性,面向對象,泛型編程。
  2. C++函數符號由 函數名+參數類型 組成,C只有函數名。所以,C沒有函數重載的概念。
  3. C++ 在 C的基礎上增加了封裝、繼承、多態的概念
  4. C++增加了泛型編程
  5. C++增加了異常處理,C沒有異常處理
  6. C++增加了bool型
  7. C++允許無名的函數形參(如果這個形參沒有被用到的話)
  8. C允許main函數調用自己
  9. C++支持默認參數,C不支持
  10. C語言中,局部變量必須在函數開頭定義,不允許類似for(int a = 0; ;;)這種定義方法。
  11. C++增加了引用
  12. C允許變長數組,C++不允許
  13. C中函數原型可選,C++中在調用之前必須聲明函數原型
  14. C++增加了STL標准模板庫來支持數據結構和算法

 一、常用的關鍵字極其用法

1.1 const 

主要用法

C++ 的const關鍵字的作用有很多,幾乎無處不在,面試中往往會問“說一說const有哪些用法”。下面是一些常見的const用法的總結:

 

 

除此以外,const的用法還有:

  • const引用可以引用右值,如const int& a = 1; 

注:

  1. const 成員方法本質上是使得this指針是指向const對象的指針,所以在const方法內,
  2. const 成員函數可以被非const和const對象調用,而const對象只能調用const 成員函數。原因得從C++底層找,C++方法調用時,會傳一個隱形的this參數(本質上是對象的地址,形參名為this)進去,所有成員方法的第一個參數是this隱形指針。const成員函數的this指針是指向const對象的const指針,當非const對象調用const方法時,實參this指針的類型是非const對象的const指針,賦給const對象的const指針沒有問題;但是如果const對象調用非const方法,此時實參this指針是指向const對象的const指針,無法賦給非const對象的const指針,所以無法調用。注意this實參是放在ecx寄存器中,而不是壓入棧中,這是this的特殊之處。在類的非成員函數中如果要用到類的成員變量,就可以通過訪問ecx寄存器來得到指向對象的this指針,然后再通過this指針加上成員變量的偏移量來找到相應的成員變量。文章來源http://blog.csdn.net/starlee/article/details/2062586/
  3. const 指針、指向const的指針和指向const的const指針,涉及到const的特性“const左效、最左右效”
  4. const 全局變量有內部鏈接性,即不同的文件可以定義不同的同名const全局變量,使用extern定義可以消除內部鏈接性,稱為類似全局變量,如extern const int a = 10.另一個文件使用extern const int a; 來引用。而且編譯器會在編譯時,將const變量替換為它的值,類似define那樣。

 

const 常量和define 的區別

  1. const常量有數據類型,而宏定義沒有數據類型。編譯器可以對前者進行類型安全檢查,而對后者只進行字符替換,沒有類型安全檢查,並且在字符替換中可能會產生意想不到的錯誤(邊際效應)。
  2. 有些集成化的調試工具可以對const常量進行調試,但是不能對宏定義進行調試。
  3. 在C++程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量。
  4. 內存空間的分配上。define進行宏定義的時候,不會分配內存空間,編譯時會在main函數里進行替換,只是單純的替換,不會進行任何檢查,比如類型,語句結構等,即宏定義常量只是純粹的置放關系,如#define null 0;編譯器在遇到null時總是用0代替null它沒有數據類型.而const定義的常量具有數據類型,定義數據類型的常量便於編譯器進行數據檢查,使程序可能出現錯誤進行排查,所以const與define之間的區別在於const定義常量排除了程序之間的不安全性.
  5. const常量存在於程序的數據段,#define常量存在於程序的代碼段
  6. const常量存在“常量折疊”,在編譯器進行語法分析的時候,將常量表達式計算求值,並用求得的值來替換表達式,放入常量表,可以算作一種編譯優化。因為編譯器在優化的過程中,會把碰見的const全部以內容替換掉,類似宏。

1.2 sizeof

  1. sizeof關鍵字不會計算表達式的值,而只會根據類型推斷大小。
  2. sizeof() 的括號可以省略, 如 sizeof a ; 
  3. 類A的大小是 所有非靜態成員變量大小之和+虛函數指針大小

1.3 static 

static的用法有:

(1)聲明靜態全局變量,如static int a; 靜態全局變量的特點:

  • 該變量在全局數據區分配內存; 
  • 未經初始化的靜態全局變量會被程序自動初始化為0(自動變量的值是隨機的,除非它被顯式初始化); 
  • 靜態全局變量在聲明它的整個文件都是可見的,而在文件之外是不可見的; 

(2)聲明靜態局部變量,即在函數內部聲明的,靜態局部變量的特點:

  • 該變量在全局數據區分配內存; 
  • 靜態局部變量在程序執行到該對象的聲明處時被首次初始化,即以后的函數調用不再進行初始化; 
  • 靜態局部變量一般在聲明處初始化,如果沒有顯式初始化,會被程序自動初始化為0; 
  • 它始終駐留在全局數據區,直到程序運行結束。但其作用域為局部作用域,當定義它的函數或語句塊結束時,其作用域隨之結束;

(3)聲明靜態函數,限定函數的局部訪問性,僅在文件內部可見

(4)類的靜態數據成員,與全局變量相比,靜態數據成員的好處有:

  • 靜態數據成員沒有進入程序的全局名字空間,因此不存在與程序中其它全局名字沖突的可能性; 
  • 可以實現信息隱藏。靜態數據成員可以是private成員,而全局變量不能;

(5)類的靜態方法

1.4 typedef 

typedef 用來定義新的類型,類似的還有#define 和 using (C++11) (應該盡可能用using ,比如 using AAA = int64_t; )

與宏定義的對比

  1. #define 在預處理階段進行簡單替換,不做類型檢查; typedef在編譯階段處理,在作用域內給類型一個別名。
  2. typedef 是一個語句,結尾有分號;#define是一個宏指令,結尾沒有分號
  3. typedef int* pInt; 和 #define pInt int* 不等價,前者定義 pInt a, b;會定義兩個指針,后者是一個指針,一個int。

1.5 inline

inline用來向編譯器請求聲明為內聯函數,編譯器有權拒絕。

與宏函數的對比

  1. 內聯函數在運行時可調試,而宏定義不可以;
  2. 編譯器會對內聯函數的參數類型做安全檢查或自動類型轉換(同普通函數),而宏定義則不會;
  3. 內聯函數可以訪問類的成員變量,宏定義則不能;
  4. 在類中聲明同時定義的成員函數,自動轉化為內聯函數
  5. 宏只是預定義的函數,在編譯階段不進行類型安全性檢查,在編譯的時候將對應函數用宏命令替換。對程序性能無影響

不能聲明為inline的函數

  1. 包含了遞歸、循環等結構的函數一般不會被內聯。
  2. 虛擬函數一般不會內聯,但是如果編譯器能在編譯時確定具體的調用函數,那么仍然會就地展開該函數。
  3. 如果通過函數指針調用內聯函數,那么該函數將不會內聯而是通過call進行調用。
  4. 構造和析構函數一般會生成大量代碼,因此一般也不適合內聯。
  5. 如果內聯函數調用了其他函數也不會被內聯。

1.6 static const \ const \ static 

1. static const 
static const 數據成員可以在類內初始化 也可以在類外,不能在構造函數中初始化,也不能在構造函數的初始化列表中初始化
2. static
static數據成員只能在類外,即類的實現文件中初始化,也不能在構造函數中初始化,不能在構造函數的初始化列表中初始化;
3. const
const數據成員只能在構造函數的初始化列表中初始化;

1.7 explicit 

explicit禁止了隱式轉換類型,用來修飾構造函數。原則上應該在所有的構造函數前加explicit關鍵字,當你有心利用隱式轉換的時候再去解除explicit,這樣可以大大減少錯誤的發生。如果一個構造函數 Foo(int) ;則下面的語句是合法的:

Foo f; 

f = 12; // 發生了隱式轉換,先調用Foo(int)用12構建了一個臨時對象,然后調用賦值運算符復制到 f 中

如果給構造函數加了explicit,即 explicit Foo(int);就只能進行顯示轉換,無法進行隱式轉換了:

f = 12; // 非法,隱式轉換

f = Foo(12); // 合法,顯示轉換

f = (Foo)12;//合法,顯示轉換,C風格

1.8 extern 

extern可以置於變量或者函數前,以標示變量或者函數的定義在別的文件中,提示編譯器遇到此變量和函數時在其他模塊中尋找其定義。此外extern也可用來進行鏈接指定

typedef 數據類型 數據類型的別名

typedef的使用非常簡單,在typedef關鍵字之后,分別跟上數據類型和相應的綽號就定義了這個數據類型的別名,在接下來的程序中就可以使用這個別名來指代這個數據類型了。

在武林中,是給大名鼎鼎的人物取外號,為的是唬人;而在C++世界中,是為那些比較復雜難以書寫的數據類型取外號,為的是偷懶。例如,覺得unsigned char無符號字符類型的書寫比較煩瑣時,可以使用typedef為它定義一個簡單易記的別名,然后使用這個別名作為數據類型來定義無符號字符類型的變量:

// 為無符號字符類型unsigned char定義一個別名uchar

typedef unsigned char uchar;

有了這個簡單的別名,就可以用它來指代無符號字符類型,用作數據類型定義變量:

// 定義一個uchar類型的變量,實際上就是unsigned char類型的變量

uchar a;

使用別名之后的程序代碼,是不是書寫起來更加簡單,同時也更加簡潔易懂呢?所以,在以后遇到類似情況的時候,使用typedef為復雜類型取一個簡單的別名,不僅自己寫起來很方便,別人讀起來也很輕松。這樣兩全其美的事情,何樂而不為呢?

這里大家可能有個疑問,利用前面學習的宏,將復雜類型定義成一個宏,不也同樣可以達到化繁為簡的目的嗎?它們兩個有什么區別呢?typedef是為復雜數據類型定義一個別名,而不只是像宏一樣簡單的替換。這一點在同時定義指針類型的多個變量時特別有用。例如,想定義兩個int*指針類型的變量,自然地,使用宏我們可能會這樣寫:

// 定義指針類型的宏
#define PINT int*
// 使用宏定義兩個變量
PINT pInt1,pInt2;

然而,這段看起來再正確不過的代碼,實際的效果卻與我們的預想大相徑庭。經過宏替換后,上面定義指針變量的代碼變為:

// 宏替換后的實際代碼

int* pInt1, pInt2;

這不是在定義兩個指針變量,而是在定義一個int指針類型變量pInt1和另一個int類型變量pInt2。想使用宏在同一行內方便地定義多個指針變量是行不通的,解決問題的辦法就是用typedef為指針類型定義一個別名,然后使用這個別名作為數據類型,就可以在一行內定義多個指針類型的變量了:

// 為指針類型int*定義一個別名PINT

typedef int* PINT;

// 同時定義多個指針類型變量

PINT pInt1, pInt2;

這時,PINT已經成為一種新的數據類型,自然它可以在同一行內同時定義多個PINT類型的變量,而這種新類型本質上還是int指針類型,也就相當於同時定義了多個int指針類型的變量。

typedef的另外一個重要用途是為復雜的類型定義簡單的別名。請看下面這行代碼:

int* (*pFunc)(int, char*);

實際上,這行代碼所定義的是一個函數指針pFunc,它所能夠指向的函數的返回值類型是int*,兩個參數分別是int類型和char*類型。如果只是定義一個這種類型的函數指針,那還可以勉強接受;如果要定義多個,那么多次書寫這么復雜的既難寫又難懂的語句,恐怕只有“撞牆”了。好在使用typedef可以為這種復雜的類型定義一個簡單的別名,使用這個別名就可以輕松定義多個這種類型的變量。

// 定義函數指針類型為PFUNC

typedef int* (*PFUNC)(int, char*);

 
// 使用PFUNC定義多個函數指針變量

PFUNC pFunc1, pFunc2;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM