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
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<Derived *>(b1); // succeeds
Derived* d2 = dynamic_cast<Derived *>(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<Derived &*>(b1); // succeeds Derived d2 = dynamic_cast<Derived &*>(b2); // fails: exception thrown
reinterpret_cast
reinterpret_cast’轉換一個指標為其它型別的指標。它也允許從一個指標轉換為整數型別。反之亦然。(譯註:是指標具體的地址值作為整數值?)
這個操作符能夠在非相關的型別之間轉換。操作結果只是簡單的從一個指標到別的指標的值的二進位制拷貝。在型別之間指向的內容不做任何型別的檢查和轉換。
如果情況是從一個指標到整型的拷貝,內容的解釋是系統相關的,所以任何的實現都不是方便的。一個轉換到足夠大的整型能夠包含它的指標是能夠轉換回有效的指標的。\
說白了就是強制型別轉換。
class A {}; class B {};
A * a = new A;
B * b = reinterpret_cast<B *>(a);
'reinterpret_cast'就像傳統的型別轉換一樣對待所有指標的型別轉換。