C++中的10個關鍵字

塵虛緣_KY發表於2016-08-30
    C++中的關鍵字有不少,參考了一些書籍和網站部落格,抽了點兒時間,這裡主要列舉了10個常碰見的,可能有些老生常談了,算是鞏固複習一下吧!
1、const
const本意是“不變的,常量”。
C語言:
(1)定義該變數為只讀變數,分配記憶體【這裡的常量只是編譯器屬性】;
(2)const與指標的組合:常量指標、指標常量、常量指標常量;
C++中:
(1)定義該變數為只讀變數,在沒有取地址&和加extern前和巨集#define作用一樣,沒有記憶體空間【常量摺疊】;
(2)類中常量需要再初始化列表裡初始化,還有引用&;static const int a=10;可以直接賦值;
(3)常量物件及常量函式。常量物件只能呼叫常量函式且不能對成員變數進行修改;如果要修改需要加mutable關鍵字修飾;
(4)引數為const,防止函式對其進行修改,返回值為const不能作為左值;
(5)在C++中將作用域限定在函式或者檔案中;
2、volatile
volatile本意是“易變的”。
    volatile提醒編譯器它後面所定義的變數隨時都有可能改變,因此編譯後的程式每次需要儲存或讀取這個變數的時候,都會直接從變數地址中讀取資料。如果沒有volatile關鍵字,則編譯器可能優化讀取和儲存,可能暫時使用暫存器中的值,如果這個變數由別的程式更新了的話,將出現不一致的現象。主要用於保護變數的安全。
一般說來,volatile用在如下的幾個地方:
(1)、中斷服務程式中修改的供其它程式檢測的變數需要加volatile;
(2)、多工環境下各任務間共享的標誌,如在多執行緒中被幾個執行緒共享的資料。應該加volatile;
(3)、儲存器對映的硬體暫存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義。
3、static
static本意是“靜態的”。
C語言:
(1)定義一個靜態變數,區域性的是改變了它的生命週期,全域性的則是該變變它的作用域,防止同名的出現;
(2)定義一個靜態函式,被定義的函式的作用域限定在當前的檔案模組中;
(3)static全域性變數初始化為0,且只初始化一次;
C++(唯一性):
(1)成員變數屬於類,不屬於任何物件,需在類外初始化;
(2)成員函式。沒有this指標,所有物件共享這一個變數;
(3)static函式不能宣告為虛擬函式;
4、mutable
mutable的本意是:“易變的,性情不定的”。
作用:當需要在const方法中修改物件的資料成員時,可以在資料成員前使用mutable關鍵字,防止出現編譯出錯。例子如下:
class CBook {
public:
    mutable double m_price;  // 如果不加就會出錯
    CBook(double price) :m_price(price) { }
    double getPrice() const; // 定義const方法
};
double CBook::getPrice() const {
    m_price = 9.8;//常量函式裡修改成員變數;
    return m_price;
}
5、explicit
explict本意是“顯示的”。
作用於類的內部建構函式上,防止一些隱式的轉換;
例如:
class A
{
	public:
	    explicit A(int _size):size(_size=){}
	private:
	   int size;
}
因為只有一個引數的建構函式是型別轉換函式,可能會發生相應的隱式型別轉換,加上explict就不會了!
A a1(12);//OK
A a2=12;//error
A a3;  //error ,沒有預設的建構函式;
a1=11;//error
a2=13;//error 不能做相應的隱形的轉換;
6、typeid-型別識別符號
    標準C++的一個新特徵是RTTI(Run-Time Type Information執行時型別資訊),它為程式在執行時確定物件型別,提供了一種標準方法。dynamic_cast和typeid。
   標準C++ 提供了typeid() 操作,以得到型別資訊,它的引數可以是一個表示式,可以是一個物件、指標或者引用,通過這個方法,可以得到一個指向常type_info物件,裡面包含了這個表示式的型別必要的資訊, 返回結果是const type_info&。不同編譯器實現的type_info class各不相同。type_info物件提供的功能有如下這些: 
    (1)、name(),可以得到一個包含型別資訊的字串,返回如:"int"、"MyClass"等; 
    (2)、before(),用來在型別列表中遍歷; 
    (3)、==操作,用來判斷型別是否相同;
      t1 == t2 如果兩個物件t1和t2型別相同,則返回true;否則返回false
      t1 != t2 如果兩個物件t1和t2型別不同,則返回true;否則返回false
      t.name() 返回型別的C-style字串,型別名字用系統相關的方法產生
      t1.before(t2) 返回指出t1是否出現在t2之前的bool值
主要作用:返回指標或者引用所指物件的實際型別。
   如果typeid的運算元不是類型別或者是沒有虛擬函式的類,則typeid指出該運算元的靜態型別。如果運算元是定義了至少一個虛擬函式的類型別,則在執行時計算型別。
例項如下:
#include <iostream>  
using namespace std;  
class father  
{  
public:  
    father()  { }  
    virtual void show()  
    {  
        cout<<"father"<<endl;  
    }  
};  
class son:public father  
{  
public:  
    son()   { }  
    void show()  
    {  
        cout<<"son"<<endl;  
    }   
};  
class grandson:public father  
{  
public:  
    void show()  
    {  
        cout<<"grandson"<<endl;  
    }  
};  
  
int main()  
{  
    father *p1=new son;  
    father *p2=new grandson;  
 
    cout<<typeid(p1).name()<<endl;  //class father *
    cout<<typeid(*p1).name()<<endl;  //class son
    cout<<typeid(*p2).name()<<endl;  //class grandson
  
    system("pause");  
    return 0;  
}  
7、export
    分離編譯模式(Separate Compilation Model)允許在一處翻譯單元(Translation Unit)中定義(define)函式、型別、類物件等,在另一處翻譯單元引用它們。編譯器(Compiler)處理完所有翻譯單元后,連結器(Linker)接下來處理所有指向 extern 符號的引用,從而生成單一可執行檔案。該模式使得 C++ 程式碼編寫得稱心而優雅。
    然而該模式卻馴不服模板(Template)。標準要求編譯器在例項化模板時必須在上下文中可以檢視到其定義實體;而反過來,在看到例項化模板之前,編譯器對模板的定義體是不處理的——原因很簡單,編譯器怎麼會預先知道 typename 實參是什麼呢?因此模板的例項化與定義體必須放到同一翻譯單元中。
   一般是在標頭檔案(*.h)中給出類的定義或全域性函式的宣告資訊,而在程式碼檔案(*.cpp)中給出具體的(類成員函式或全域性函式的)函式定義。然後在多個使用者程式碼(use.cpp)檔案中包含該標頭檔案後,就可以使用其中定義或宣告的類和函式。標頭檔案中一般不包含變數、結構和類物件的定義,因為這樣可能會導致重複定義的編譯錯誤。解決辦法是,在某個程式碼檔案中進行定義,對普通型別(包括基本資料類、結構和類),在其他使用者程式碼檔案中用關鍵字extern來引用它們。
   但是對模板型別,則可以在標頭檔案中,宣告模板類和模板函式;在程式碼檔案中,使用關鍵字export來定義具體的模板類物件和模板函式;然後在其他使用者程式碼檔案中,包含宣告標頭檔案後,就可以使用該這些物件和函式了。
例如:
普通內建型別:
extern int n;
extern struct Point p;
extern class A a;
模板型別:
export template<class T> class Stack<int> s;
export template<class T> void f (T& t) {……}
例如:

// out.h:(宣告標頭檔案——只包含out函式的宣告資訊)
template<class T> void out (const T& t);
// out.cpp:(定義程式碼檔案——包含out函式的宣告[通過include]和定義等全部資訊)
#include <iostream>
#include “out.h”
export template<class T> void out (const T& t) {std::cerr << t;}
//user.cpp:(使用者程式碼檔案——包含函式的宣告標頭檔案後就可以使用該函式)
#include “out.h”
補充:在linux下的export:
主要的作用是將區域性變數-SET轉換為環境變數-ENV。

   export 也是 bash 的一個內建命令。它主要是用來將父 shell 裡的變數匯出供子 shell 使用。登入主機後,在執行Bash Script之前,其實我們已經處於一個BashShell中。這個Shell叫login Shell,是將來我們執行任何Script的上層環境,又叫父SHell。父Shell會根據Script程式的第一行#!之後指定的Shell程式開啟一個子Shell環境,然後在子Shell中執行此 Shell Script。一旦子Shell中的Script執行完畢,此子Shell隨即結束,回到父Shell中,不會影響父Shell原本的環境。子Shell環境擁有與父Shell相同的環境變數、標準輸入、輸出、錯誤等。
這   裡注意上面說的是環境變數而不是區域性變數,如果是區域性變數,在子shell中又要使用,這時候export就派上用場了!
作用如下:
1. 用 export 匯出的變數放在“匯出變數列表”中,它可以被子 shell (子 shell 的子 shell 也是如此)拷貝並使用。
2. 被 export 出來的變數雖然可以被子 shell 使用,但它也只是一個拷貝,而不會影響到父 shell 中的值以及其它子 shell 中的值。
8、asm
作用:程式程式碼內嵌彙編

#include "stdafx.h"
void main()
{
	int var = 1;
	int temp = var;
	printf("var=%d\n",var);
	__asm
	{
	     mov  dword ptr [ebp-4], 10h
	}
	int ret = var;
	printf("var= %d\n",ret);
}
mov  dword ptr [ebp-4], 10h 的含義如下:
   mov是傳送指令 把十六進位制的10(也就是十進位制的16)傳送給dword ptr [ebp-4]~如果用的是vc6的話~設斷點->除錯->alt+8可以看到ptr [ebp-4]其實是var的地址~也就是改變了var的值~
9、#define和typedef的區別
#define:
是巨集定義,巨集定義就是單純的替換,把錢替換成麵包,把黑板擦替換成抹布,已經不是原來的東西了。
(1)在編譯期間起作用,沒有記憶體空間編譯期間巨集的名稱沒有進入符號表,這也是儘量用const和inline替換#define的原因之一;
(2)沒有資料型別;
typedef:別名,徹底的封裝,不容改變。指向的是同一個空間,名字不同罷了!
例如:一個人有中文名,有小名,有外國名等等,但都是一個人!
區別:
(1)#define能用其他的型別進行擴充套件,但是typedef不行
  #define INT int
  typedef int TYPEINT;
  unsigned INT num;  //OK
  但是
  unsigned TYPEINT num;//ERROR;  //徹底的封裝,不容改變
(2)typedef能保證變數型別的一致,但是#define不行
#defined  PINT int *
typedef int* pINT;
PINT a,b;//等價於:int *a,b; 也就是int *a; int b; a、b的型別不一致;
pINT a,b;//等價於:int *a; int *b; a、b的型別一致;
10、inline
行內函數:
注意:inline只是程式設計師對編譯器的建議,有inline不一定是inline函式,但是沒有inline一定不是inline函式。
  許多書上都會提到這是因為編譯器比絕大多數程式設計師都更清楚函式呼叫的開銷有多大,所以如果編譯器認為呼叫某函式的開銷相對該函式本身的開銷而言微不足道或者不足以為之承擔程式碼膨脹的後果則沒必要內聯該函式。內聯使用不恰當是會有副作用的:會帶來程式碼膨脹。行內函數本來是希望用稍微的程式碼膨脹換取時間效率的,可是現在程式碼膨脹了,效率一樣沒提高!!
行內函數和巨集很類似,而本質區別在於:
  巨集是由前處理器對巨集進行替代,而行內函數是通過編譯器控制來實現的,直接將響應的函式程式碼替換函式呼叫。而且行內函數是真正的函式(進行型別檢查),只是在需要用到的時候,行內函數像巨集一樣的展開,所以取消了函式的引數壓棧,跳轉(來回跳躍並記錄跳躍位置),減少了呼叫的開銷。你可以象呼叫函式一樣來呼叫行內函數,而不必擔心會產生於處理巨集的一些問題。



資料參考:
http://www.cnblogs.com/onlycxue/archive/2012/12/22/2828863.html
http://blog.csdn.net/hikaliv/article/details/4474835
http://bbs.csdn.net/topics/350185366

相關文章