程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++部分關鍵字總結

C++部分關鍵字總結

編輯:關於C++

auto
C++11引入的auto關鍵字實現類型退到,通過這個關鍵字不僅能方便地獲取復雜的類型,而且還能簡化書寫,提高編碼效率。

auto x = 5;   //OK
auto pi = new auto(1);  //OK
const auto *v = &x, u = 6;  //OK
auto int r;   //error:auto不在表示存儲類型指示符,這也是新更改的特性
auto str;     //error:無法推導出str類型

Note:v與u的推導需要注意兩個小地方
1)雖然經過前面const auto *v = &x 的推導,auto的類型可以確定為int,但是u仍然必須要寫後面的“=6”,否則編譯不通過。
2)U的初始化不能使編譯器推導產生二義性。ep:u = 6改為 u = 6.0, 編譯器會報錯。

auto並不代表實際的類型聲明,僅僅是一個類型聲明的“占位符”。

使用auto聲明的變量必須馬上初始化,讓編譯器推導出它的實際類型,並在編譯時將auto占位符替換為真正的類型。

在C++11之前,auto 表示“具有自動存儲期的局部變量”,不過其實它在這方面的作用不大,auto int i = 1; 對這個我們再熟悉不過了。不寫auto也是一樣的效果就是為了跟static int j = 1;區分開來而已。

auto推導可以和指針,引用結合起來使用,還可以帶上const,volatile限定符。

int x = 1;
const auto e =x;     //const int
auto f = e;         //int
const auto& g = x;   //const int&
auto& h= g;    //const int& 

從上面的例子不難看出:
1)當不聲明為指針和引用時,auto的推導結果和初始化表達式拋棄引用和CV限定符後類型一致。
2)當聲明為指針或者引用時,auto的推導結果將保持初始化表達式的cv屬性。

decltype
decltype關鍵字,用來在編譯時推導出一個表達式的類型。語法格式:

decltype(exp)
int x = 0;
decltype(x) y = 1;   // y->int
const int& i = x;    //i->const int &
decltype(i) j = y;    //j->const int &
const decltype(y) * p =&z;  // p->const int *
decltype(x) * pi = &x;   //*pi->int
decltype(pi)* pii = &pi   //*pii->int *

從上面的例子不難看出decltype的推導規則:
1)exp是標示符,雷訪問表達式,decltype(exp)和exp類型一致
2)exp是函數調用,decltype(exp)和返回值的類型一致
3)若exp是一個左值,則decltype(exp)是exp類型的左值引用,否則和exp類型一致。

decltype與引用

int i = 42;
int *p = &i:
decltype(*p) c;//error:c是int &必須初始化

所以如果表達式是解引用的操作,則decltype得到引用類型。解引用可以得到指針所指的對象,而且還能給這個對象賦值。

decltype與auto還有一個重要的區別:decltype與表達式的形勢密切相關。對於decltype所用的表達式來說,如果變量名加上了一對括號,則得到的類型與不加括號是不一樣的。也就是說decltype使用一個不加括號的變量,則得到的結果就是該變量的類型。而加上一層或者多層括號的是作為一個特殊的左值表達式。得到的是引用類型。

decltype(i) = e;  //OK
decltype((i)) = e;  //error:e為引用類型必須初始化 

explicite

explicit使用注意事項:

*explicit 關鍵字只能用於類內部的構造函數聲明上。

*explicit 關鍵字作用於單個參數的構造函數。

在C++中,explicit關鍵字用來修飾類的構造函數,被修飾的構造函數的類,不能發生相應的隱式類型轉換,只能以顯示的方式進行類型轉換。

explicit關鍵字只能用於類內部的構造函數聲明上。在類的定義體外部所做的定義上不能再重復它。

如果一個類或結構存在多個構造函數時,explicit 修飾的那個構造函數就是默認的

當構造函數被聲明為explicit時,編譯器將不使用它作為轉換操作符。

explicit 關鍵字作用於單個參數的構造函數。

通常,除非有明顯的理由想要定義隱式轉換,否則,單形參構造函數應該為explicit。

inline
1) inline表示內聯,即在函數調用出將函數內聯地展開。

2) inline既可以出現在類定義內部,也可以出現在外部。當在外部出現時必須定義在類定義的頭文件中,因為調用函數時需要看到函數是什麼樣子。

3)inline修飾的函數是在編譯時期。

constexptr

這個constexptr函數指能用於常量表達式的函數。定義為constexptr的函數的方法與其它函數類似。不過要遵循:函數的返回類型及所有形參的類型都得是字面值類型,而且函數體中必須有且只有一條return語句:

constexptr int new_sa() {return 42;}
constexptr int foo = new_sa();//OK

我們把new_sz定義成無參數的constexpr函數。因為編譯器能在程序編譯時驗證new_sz函數的返回的是常量表達式,所有可以用new_sz函數初始化constexpr類型的變量foo。

執行該初始化任務時,編譯器把對constexpr函數的調用替換成其結果值。為了能在編譯過程中隨時展開,constexpr函數被隱式的定義為內聯函數。

constexpr函數體內也可以包含其他語句,只要這些語句在運行時不執行任何操作就行。例如,constexpr函數中可以有空語句、類型別名以及using聲明。

我們允許constexpr函數的返回值並非一個常量:

//如果arg是常量表達式,則scale(arg)也是常量表達式

constexpr size_t scale(size_t cnt) {return new_sz()*cnt;}

當scale的實參是常量表達式時,它的返回值也是常量表達式:反之則不然:

int arr[scale(2)]; //正確:scale(2)是常量表達式

int i=2; //i不是常量表達式

int a2[scale(i)]; //錯誤:scale(i)不是常量表達式

如上例所示,當我們給scale函數傳入一個形如字面值2的常量表達式時,它的返回類型也是常量表達式。此時,編譯器用相應的結果值替換對scale函數的調用。

如果我們用一個非常量表達式調用scale函數,比如int類型的對象i,則返回值是一個非常量表達式。當把scale函數用在需要常量表達式的上下文中時,由編譯器負責檢查函數的結果是否符合要求。如果結果恰好不是常量表達式,編譯器將發出錯誤信息。

constexpr函數不一定返回常量表達式

我也有點雲裡霧裡的。大家看著理解吧。

把內聯函數和constexpr函數放在頭文件內

和其他函數不一樣,內聯函數和constexpr函數可以再程序中多次定義。畢竟,編譯器要想展開函數僅有函數聲明是不夠的,還需要函數的定義。不過,對於某個給定的內聯函數或者constexpr函數來說,它的多個定義必須定義一致。基於這個原因,內聯函數和constexpr函數通常定義在頭文件中。

friend

1) 類的friend函數可以訪問類的private和protected成員。

2) friend關系不能繼承,基類的友元對子類的成員沒有特殊訪問權限,如果基類被授予友元關系,則只有基類具有特殊訪問權限,其派生類不能訪問授予友元關系的類。

3) 派生類的friend函數可以訪問派生類的一切變量,包括從基類繼承下來的protected域中的變量,但對父類來說它不是friend。也就是說friend和類本身的訪問權是100%。

4) friend關鍵字只能出現在類定義內部,不可出現在外部。

#include 
  #include 

  class Point
  {
  public:
    Point(double xx, double yy) { x=xx; y=yy; }
    void Getxy();
    friend double Distance(Point &a, Point &b);
  private:
    double x, y;
  };


  double Distance(Point &a, Point &b)
  {
  double dx = a.x - b.x;
  double dy = a.y - b.y;
  return sqrt(dx*dx+dy*dy);
  }

static
在C++中此關鍵字用途更多。

1) static類成員屬於整個類,不屬於某個類對象。

2) static成員遵循正常的公有/私有訪問規則。

3) 對於public static成員可以通過類名或對象名訪問,對於private static成員則不可,須按照正常private成員訪問規則進行訪問。

(4) static成員函數可以訪問static成員函數和變量,不可訪問非static成員和函數,因為static成員是類的,而非static是對象的,類的產生先於對象,怎麼能在對象為產生之間就調用它的數據呢。

5) 非static成員函數既可以訪問static成員又可以訪問非static成員。

6) 可以通過類名直接調用public static成員,或者通過對象名、引用或指針間接調用。注意此處指的是public static成員,不是private。

7) static成員函數沒有this指針,因為this指向的是對象。

8) static成員函數不能聲明為const或虛函數,因為此二者都是針對對象而言,此外static函數實際上是”加上了訪問控制的全局函數”,全局函數當然沒有虛函數。但是static變量可以聲明為const。

9) static只用於類內聲明,在類外定義時不能加static。

10) static變量的初始化只能放在源文件中,如果放在頭文件中可能會被重復定義。

11) 在對static變量進行初始化時,可以忽略任何存取權限的束縛,例如:

double ClassA::m_a = 0.05; (注意假設ClassA::m_a是private, 則除了在定義static時可以這樣寫外,其它地方均不得如此)

12) static成員變量不能再類內初始化,必須在類外定義時初始化(在源文件中),但是有個例外,const static成員可以在類內初始化,同時仍必須在類外進行定義,定義時無需加static,無需指定初始值,但必須加const修飾符。[這一條引自C++ Primer,4th Edition, P401,但是上機實驗證明,如果已經在類內初始化,則就不須在類外定義,否則產生多重定義的變異錯誤,不知道是不是編譯器的問題,我用的是VS2005]

13) static成員可以被繼承,父類的static變量和函數在派生類中依然可用,但是受訪問控制,而且對static變量來說,派生類和父類中的static變量是共用空間的,這點在利用static變量進行引用計數時要注意。

靜態成員函數不能夠使用const和volatile修飾。並且靜態成員函數只能訪問靜態成員變量,在靜態成員函數中不能使用this。

register

這個關鍵字命令編譯器盡可能的將變量存在CPU內部寄存器中而不是通過內存尋址訪問以提高效率。

volatile
volatile的本意是“易變的”,volatile關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改,比如操作系統、硬件或者其它線程等。遇到這個關鍵字聲明的變量,編譯器對訪問該變量的代碼就不再進行優化,從而可以提供對特殊地址的穩定訪問。

當要求使用volatile 聲明的變量的值的時候,系統總是重新從它所在的內存讀取數據,即使它前面的指令剛剛從該處讀取過數據。而且讀取的數據立刻被寄存。

volatile int i=10;

int a = i;

。。。//其他代碼,並未明確告訴編譯器,對i進行過操作

int b = i;

volatile 指出 i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,因而編譯器生成的匯編代碼會重新從i的地址讀取數據放在b中。而優化做法是,由於編譯器發現兩次從i讀數據的代碼之間的代碼沒有對i進行過操作,它會自動把上次讀的數據放在b中。而不是重新從i裡面讀。這樣以來,如果i是一個寄存器變量或者表示一個端口數據就容易出錯,所以說volatile可以保證對特殊地址的穩定訪問。

const_cast

從類中移除 const、volatile特性。

指向任何對象類型的指針或指向數據成員的指針可顯式轉換為完全相同的類型(const、volatile 和 __unaligned 限定符除外)。 對於指針和引用,結果將引用原始對象。 對於指向數據成員的指針,結果將引用與指向數據成員的原始(未強制轉換)的指針相同的成員。 根據引用對象的類型,通過生成的指針、引用或指向數據成員的指針的寫入操作可能產生未定義的行為。
您不能使用 const_cast 運算符直接重寫常量變量的常量狀態。
const_cast 運算符將 null 指針值轉換為目標類型的 null 指針值。

const

const 是一個左結合的類型修飾符,它與其左側的類型修飾符和為一個類型修飾符。const可以用於定義常量,可以限定函數的引用參數(因為傳值的參數根本不用限定),可以限定函數返回值為引用的情況。還有一個用法是修飾類的成員函數。這樣情況下,在類內的聲明和類外的定義都要加上const。

還有一種情況是,聲明類的const成員變量的時候,如何進行初始化。

在這種情況下,由於常量不能修改,所以只能在構造函數的初始化列表中進行復制初始化。如果同時聲明為了static時可以在類外進行初始化,但此時不能加static關鍵字。

virtual

在基類中被定義為virtual的函數,派生類重載該函數不需要再次顯示說明該函數是virtual的。

inline, static, constructor三種函數都不能帶有virtual關鍵字。

inline是編譯時展開,必須有實體;

static屬於class自己的,也必須有實體;

virtual函數基於vtable(內存空間),constructor函數如果是virtual的,調用時也需要根據vtable尋找,但是constructor是virtual的情況下是找不到的,因為constructor自己本身都不存在了,創建不到class的實例,沒有實例,class的成員(除了public static/protected static for friend class/functions,其余無論是否virtual)都不能被訪問了。

static_cast

該運算符把exdivssion轉換為type-id類型,但沒有運行時類型檢查來保證轉換的安全性。它主要有如下幾種用法:
1)用於類層次結構中基類和子類之間指針或引用的轉換。
  進行上行轉換(把子類的指針或引用轉換成基類表示)是安全的;
  進行下行轉換(把基類指針或引用轉換成子類表示)時,由於沒有動態類型檢查,所以是不安全的。
2)用於基本數據類型之間的轉換,如把int轉換成char,把int轉換成enum。這種轉換的安全性也要開發人員來保證。
3)把空指針轉換成目標類型的空指針,把任何類型的表達式轉換成void類型。

注意:static_cast不能轉換掉exdivssion的const、volitale、或者__unaligned屬性。

dynamic_cast

dynamic_cast’只用於對象的指針和引用。當用於多態類型時,它允許任意的隱式類型轉換以及相反過程。不過,與static_cast不同,在後一種情況裡(注:即隱式轉換的相反過程),dynamic_cast會檢查操作是否有效。也就是說,它會檢查轉換是否會返回一個被請求的有效的完整對象。
檢測在運行時進行。如果被轉換的指針不是一個被請求的有效完整的對象指針,返回值為NULL。

class Base { virtual dummy() {} }; class Derived : public Base {};  
Base* b1 = new Derived; Base* b2 = new Base;  
Derived* d1 = dynamic_cast(b1); // succeeds 
Derived* d2 = dynamic_cast(b2); // fails: returns 'NULL'  
如果一個引用類型執行了類型轉換並且這個轉換是不可能的,一個bad_cast的異常類型被拋出: 代碼: 
class Base { virtual dummy() {} }; class Derived : public Base { };  
Base* b1 = new Derived; Base* b2 = new Base;  
Derived d1 = dynamic_cast(b1); // succeeds Derived d2 = dynamic_cast(b2); // fails: exception thrown

reinterpret_cast
reinterpret_cast’轉換一個指針為其它類型的指針。它也允許從一個指針轉換為整數類型。反之亦然。(譯注:是指針具體的地址值作為整數值?)
這個操作符能夠在非相關的類型之間轉換。操作結果只是簡單的從一個指針到別的指針的值的二進制拷貝。在類型之間指向的內容不做任何類型的檢查和轉換。
如果情況是從一個指針到整型的拷貝,內容的解釋是系統相關的,所以任何的實現都不是方便的。一個轉換到足夠大的整型能夠包含它的指針是能夠轉換回有效的指針的。\

說白了就是強制類型轉換。

class A {}; class B {}; 
A * a = new A; 
B * b = reinterpret_cast(a); 
'reinterpret_cast'就像傳統的類型轉換一樣對待所有指針的類型轉換。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved