1. 程式人生 > >C/C++基礎----特殊工具和技術 (過載new和delete,RTT,限定作用域的列舉型別,類成員指標,巢狀類,區域性類,volatile,連結指示 extern “C”)

C/C++基礎----特殊工具和技術 (過載new和delete,RTT,限定作用域的列舉型別,類成員指標,巢狀類,區域性類,volatile,連結指示 extern “C”)

過載new和delete

1呼叫operator new( 或new[])標準庫函式分配足夠大的、原始的、未命名的記憶體空間以便儲存特定型別的物件
2編譯器執行相應地建構函式以構造這些物件,併為其傳入初始值
3返回一個指向該物件的指標

可以在全域性作用域定義operator new,也可以定義為成員函式
如果是類型別,首先在本類及其基類中查詢,否則在全域性域中查詢,最後使用標準庫定義版本。
//這些版本可能丟擲異常
void operator new(size_t);
void
operator new[] (size_t);
void operator delete(void) noexcept;
void operator delete[] (void

) noexcept;
//這些版本承諾不丟擲異常
void operator new(size_t, nothrow_t&) noexcept;
void
operator new[] (size_t, nothrow_t&) noexcept;
void operator delete(void, nothrow_t&) noexcept;
void operator delete[] (void, nothrow_t&) noexcept;
nothrow_t是定義在new標頭檔案的一個struct,不包含任何成員。

當定義成類的成員時,是隱式靜態的。
自定義operator new可以提供額外的引數。此時用到這些自定義函式的new表示式必須使用new的定位形式,將實參傳給新增的形參。
void* operator new(size_t, void)形式不允許過載,其他都可以。
operator delete返回型別必須void

,第一個形參必須void*。如果是定義為類的成員,還可以包含另外一個size_t的形參。

過載不能改變new和delete運算子的基本含義。
operator new和delete必須以某種方式執行分配記憶體與釋放記憶體的操作。
定位new
new(place_address) type
new(place_address) type (initializers)
new(place_address) type [size]
new(place_address) type [size] {braced initializer list}
place_address必須是指標,定位new呼叫void* operator new(size_t, void*)這是一個我們無法自定義的operator new版本
該函式不分配任何記憶體,它只是簡單地返回指標實參;然後由new表示式負責在指定地址初始化物件。允許我們在一個特定的、預先分配的記憶體地址上構造物件。

定位new與allocator區別
傳給construct的指標必須指向同一個allocator物件分配的空間
但是傳給定位new的指標無須是operator new分配的記憶體

RTTI

typeid運算子,返回表示式的型別
dynamic_cast運算子,將基類指標或引用安全地轉換成派生類的指標或引用
特別適用於我們想使用基類物件的指標或引用執行某個派生類操作並且該操作不是虛擬函式。
潛在風險:必須清楚地知道轉換的目標型別,並且必須檢查型別轉換是否被成功執行。
dynamic_cast<type> (e);
dynamic_cast<type&> (e);//e必須左值
dynamic_cast<type&&> (e);//e不能左值
指標型別轉換失敗,返回0;引用型別轉換失敗,丟擲一個bad_cast
if(Derived
dp=dynamic_cast<Derived*> (bp))
{}
else
{}
在條件語句中執行操作,可以確保型別轉換和結果檢查在同一條表示式中完成。dp在外部不可訪問,即時忘了做相應判斷,也確保安全。

typeid
作用域物件,結果是一個常量物件的引用。
頂層const忽略;如果是引用返回引用所引物件的型別;陣列或函式不做指標轉換
typeid是否需要執行時檢查決定了表示式是否會被求值。

虛擬函式必須具有相同的形參,所以只能使用基類的成員,而不能使用派生類獨有的。
使用typeid判斷,再使用dynamic_cast轉換
type_info沒有預設構造,拷貝移動構造及賦值運算都定義為刪除的。建立物件的唯一途徑是使用typeid運算子。

其他

  • 限定作用域的列舉型別,遵循常規作用域準則

int I = color::red;//不限定作用域的列舉隱式地轉換成整型
Int j= peppers::red;//限定作用域的列舉不會進行隱式轉換

  • 前置宣告

不限定作用域的必須制定成員型別,限定作用域的可以使用預設的int
enum intValues : unsigned long long;
enum class open_modes;

  • 類成員指標

指向類的非靜態成員的指標,並沒有指向任何資料,只有當解引用時才提供物件的資訊。
成員函式與指向成員函式的指標之間不存在自動轉換規則。
可以使用類型別名,做成成員指標函式表,使程式碼更易讀寫

用作可呼叫物件

1function<bool (const string&)> fcn =&string::empty;
find_if(svec.begin(), sevc.end(), fcn);
function<bool (const string)> fcn =&string::empty;
find_if(pvec.begin(), pevc.end(), fcn);
使用function為成員函式生成一個可呼叫物件必須先翻譯程式碼,使隱式形參變成顯示
可以理解為類的成員函式有兩個過載版本,需要確定用哪一個
2mem_fn
svec.begin());
f(&svec[0]);
含有一對過載的函式呼叫運算子
3bind
auto it =find_if(svec.begin(), sevc.end(), bind(&string::empty, _1);

  • 巢狀類

巢狀類的名字在外層類作用域中是可見的,外層類作用域之外不可見
巢狀類必須宣告在類的內部,但是可以定義在類的內部或外部
如聲明瞭靜態成員,定義需在外層類的作用域外
不在類的作用域內需要加字首

  • union

union不能含有引用型別的成員,預設公有
可以定義成員函式,但不能繼承自其它類也不能作為基類,不能含有虛擬函式
匿名union,編譯器自動為該union自動建立一個未命名物件。定義所在作用域內可以直接訪問其成員。
對於union,構造和銷燬類型別成員必須執行復雜的操作,通常使用類來管理

  • 區域性類
定義在某個函式內部,所有成員必須完整定義在類的內部,不能有靜態資料成員。
只能訪問外層作用域定義的型別名、靜態變數以及列舉型別
  • 不可移植特性
位域,成員名字之後緊跟冒號以及一個常量表達式,最好設為無符號型別
volatile跟const用法類似,只有volatile成員函式才能被volatile物件呼叫
區別,不能使用合成的拷貝移動建構函式及賦值運算初始化化volatile物件或從volatile物件賦值。合成的成員接受的形參型別是(非volatile常量引用)

連結指示 extern “C”

需要呼叫其他語言編寫的函式,也必須在C++中進行宣告,且必須指定返回型別和形參列表
可單個可複合
extern “C”
{
int strcmp(const char*, const char*);
}
多重宣告可以應用於整個標頭檔案,連結指示可以巢狀
語言是函式型別的一部分,C函式的指標與C++函式的指標是不一樣的型別
extern “C” void f1(void(*)(int));
連結指示對整個宣告都有效,包括形參。
想在C++函式中傳入C函式的指標,必須使用類型別名

C和C++中編譯同一個原始檔,可以在編譯C++版本的程式時前處理器定義__cplusplus(兩個下劃線),條件編譯
#ifdef __cplusplus
extern “C”
#endif
int strcmp(const char*, const char*);