C 必知必會(4)

2021-06-22 06:26:41 字數 4372 閱讀 8014

直接呼叫建構函式是行不通的,然而可以通過使用placement new來哄騙編譯器呼叫建構函式:

void *operatornew(size_t, void* p) throw()

placement new是operator new的乙個標準的過載版本,也位於全域性名字空間中,但和我們通常看到的operator new不同,語言明令禁止使用者替換placement new,而普通的operator new和operator delete則可以被替換掉,只不過我們往往不該那樣做而已。placement new的實現忽略了表示大小的實參,直接返回其第二個實參。placement new允許我們在乙個特定的位置放置物件,起到了呼叫乙個建構函式的效果:

class sport;     //表示乙個串列埠

const int comloc= 0x00400000;    //乙個串列埠的空間位置

void *comaddr =reinterpret_cast(comloc);

sport* com1 =new (comaddr) sport;   //在comloc位置建立物件

區分new操作符和命名為operatornew的函式過載很重要。new 操作符不可以被過載,所以其行為總是一樣的。它呼叫乙個名為operator new的函式,然後初始化返回的儲存區。我們希望對記憶體分配行為進行任何改變,均需要通過operator new的不同過載版本實現,而非通過new操縱符實現。同樣的道理也適用於delete操作符和operator delete操作符。

placement new是函式operatornew的乙個版本,它並不實際分配任何儲存區,僅僅返回乙個指向已經分配好空間的指標。正因為呼叫placement new並沒有分配空間,所以不要對其進行delete操作,記住這一點很重要。

delete com1;              //哎呀!

然後,儘管沒有分配任何儲存區,但卻是建立了乙個物件,這個物件應該在其生命結束時銷毀。為此,應該避免使用delete操作符,代之一直接呼叫該物件的析構函式:

com1->~sport();  //呼叫析構函式而非delete操作符

也可以使用placement arranew在給定的空間位置建立物件陣列:

const intnumcoms = 4;

sport *comports= new (comaddr) sport[numcoms];      //建立陣列

當然,這些陣列元素最終必須銷毀:

int i=numcoms;

while(i)

comports[--i].~sport();

string* sbuf =new string[bufsize];      //呼叫預設建構函式

int size = 0;

//剛才的預設構造動作白做了!

如果只使用了陣列的一部分元素,或者元素被立即賦值,那麼以上做法效率會很低。更糟糕的是,如果陣列的元素型別沒有預設建構函式,將會產生乙個編譯錯誤。

placement new通常用於解決此類緩衝區問題。採用這種方式,緩衝區占用的儲存區的分配,可以避免被預設的建構函式初始化:

const int size_tn = sizeof(string) * bufsize;

string* sbuf =static_cast(::operator new(n));

int size = 0;

在第一次訪問陣列元素時,不能為其賦值,因為他還沒有被初始化。然而,可以用placement new通過複製建構函式來初始化元素。

new (&buf[size++]) string(val);              //placement new

通常使用placement new也需要做一些清理工作:

voidcleanbuf(string buf, int size)

while(size)

buf[--size].~string();          //銷毀已初始化的元素

::operator delete(buf);             //釋放儲存區

我們無法對new操作符和delete操作符做什麼,因為他們的行為是固定的,但可以改變他們所呼叫的operator new和operator delete。做這件事情的最佳方式是為類宣告operator new和operator delete。做這件事情的最佳方式是為類宣告operator new和operator delete成員函式:

成員operator new和operatordelete是靜態成員函式,我們可以回想起靜態成員函式沒有this指標。由於這兩個函式進負責獲取和釋放物件的儲存區,因此它們用不著this指標。像其他靜態成員函式一樣,它們可以被繼承。

如果在基類中定義了成員operatornew和delet,要確保基類析構函式是虛擬的。否則通過乙個基類指標來刪除派生類物件的結果就是未定義的。

乙個常見的誤解是以為使用new和delete操作符就意味著使用堆記憶體,其實並非如此。使用new操作符唯一能表明的是名為operator new操作符將被呼叫,且該函式返回乙個指向某塊記憶體的指標。標準,全域性的operator new和operator delete的確是從堆上分配記憶體,但成員函式operator new和operator delete可以做他們想做的事情,對於分配記憶體到底來自**沒有任何限制。

對陣列而言,new表示式不是使用operatornew來為陣列分配記憶體,而是使用array new。類似地,delete表示式也不是呼叫operator delete來釋放陣列的儲存區,而是呼叫array delete。可以重新宣告array new和array delete,如下:

void* operatornew(size_t ) throw(bad_alloc);              //arraynew

void operatordelete(void*) throw();  //array delete

公理1:異常是同步的並且只能發生在函式呼叫的邊界

諸如預定義型別的算術操作,預定義型別(尤其是指標)的賦值以及其他底層操作不會導致異常發生。

但操作符過載和模板使得情況變為複雜,因為很難判斷乙個給定的操作操作是否會導致乙個函式呼叫並可能丟擲異常。

公理2:物件的銷毀時異常安全的

禁止在析構函式中和銷毀物件時丟擲異常

公理3:交換操作(swap)不會丟擲異常

string &string::operator = (const char*str)

if(!str)str=」」;

char*tmp = strcpy(new char[strlen(str)+1], str);

deletes_;

s_= tmp;

return*this;

這個函式看上去好像裝飾過度了,本可以取消臨時變數而採用更少行為**來實現它。

string&string::operator = (const char* str)

delete s_;

if(!str) str=」」;

s_= strcpy(new char[strlen(str)+1], str);

return *this;

然而,儘管array delete有公理保證,即物件的銷毀時異常安全的。但是array new沒有這樣的承諾。如果在不清楚新緩衝區是否被成功分配之前就delete掉原來的緩衝區,可能就會使string物件變得很糟糕。

來解決這個問題的方法:首先做任何可能會丟擲異常的事情(但不會改變物件的重要狀態),然後以不會丟擲異常的操作作為結束。

string::operator=的第乙個實現就死這麼做的。

raii表示「資源獲取即初始化「。raii的基本技術原理很簡單:如果希望對某個重要資源進行跟蹤,那麼建立乙個物件,並將資源的生命期和資源的生命期相關聯。如此一來,就可以利用c++複雜老練的物件管理設施來管理資源。最簡單的raii形式是,建立這乙個物件,使其建構函式獲取乙份資源,而析構函式則釋放這份資源:

class resource;

classresourcehandle         //獲取資源

~resourcehandle()       //釋放資源

resource *get()   //訪問資源

private:

resourcehandle(constresourcehandle &);

resourcehandle&operator = (const resourcehandle &);

resource*r_;

考慮以下未使用raii的簡單**:

void f();    //這是乙個針對指向字元顯示特化的版本。

其中的模板引數列表是空的,但要特化的模板實參則附加在模板名字後面。這個顯式特化版本其實並不是乙個模板,因為此時沒有剩下任何未指定的模板引數。處於這個原因,類模板顯式特化通常被稱為「完全特化」,以便區分區域性特化區分開,後者是乙個模板。

此外,c++沒有顯式要求特化的介面必須和主模板的介面完全匹配,是上上,我們可以增加或減少介面。

MySQL必知必會 4

每個sql語句都是由乙個或多個關鍵字構成的,select語句的用途是從乙個或多個表中檢索資訊。結束sql語句 多條sql語句必須以分號 分隔。如果你使用的是mysql命令列,必須加上分號來結束sql語句。sql語句和大小寫 請注意,sql語句不區分大小寫,因此select與select是相同的。同樣...

C 必知必會

條款2 多型 2 條款3 設計模式 5 條款4 stl 8 條款5 引用是別名而非指標 10 條款6 陣列形參 13 條款7 常量指標與指向常量的指標 16 條款8 指向指標的指標 19 條款9 新式轉型操作符 21 條款10 常量成員函式的含義 25 條款11 編譯器會在類中放東西 29 條款12...

SQL必知必會》 筆記4

插入完整行insert into customers values 1000000006 toy land 123 any street new york ny 11111 usa null,null 一一對應的屬性 insert into customers cust id,cust name,c...