關于placement new
在https://blog.csdn.net/qq_42604176/article/details/111997397中已經介紹了placement new的形式。
它的形式為new()/delete().我們將分配好內存的指針送入括號中,就完成了初步的調用了。
其實我們可以定義放任何的東西到()內部。只放一個指針的版本是的new()是標準庫先寫好給我們的。
我們可以重載operator new,并寫出多個版本,如:
Foo* pf = new(300,'c')Foo; //注意,這里沒有傳入指針
前提是每一個版本的聲明都必須由獨特的參數列,其中第一個參數必須是size_t,這是因為當沒有()時,進行的是new Foo操作,Foo的大小會被傳進operator new中作為第一參數,Foo的大小是個size_t類型。所以我們寫的各種各樣的版本也必須遵循這個規則。第二第三參數等等可由自己設計。new()括號中的就是第二第三參數,他們可以指定placement arguments 為初值。
下面是實例:
class Foo {
public:Foo() {cout << "Foo::Foo()" << endl; };Foo(int) {cout << "Foo::Foo()" << endl; throw Bad();} //這里故意拋出異常,用來測試 placement operator delete//【1】一般的operator new()的重載void* operator new(size_t size) {return malloc(size);}//【2】這個是標準庫已提供的placement new()的重載形式void* operator new(size_t size, void* start) {return start;}//【3】這個是我們重載的 placement new void* operator new(size_t size, long extra) {return malloc(size + extra);}//【4】這個也是我們重載的 placement newvoid* operator new(size_t size, long extra, char init) {return malloc(size + extra);}//【5】這個也是我們重載的,不過我們故意寫錯定義參數的類型void* operator new(long extra, char init) {return malloc(extra);} //很顯然這個版本會報錯
};
關于placement delete
我們也可以重載placement operator delete,并對應著placement operator new寫出多個對應版本,但他們絕對不會被delete調用。
只有當new所調用的ctor拋出異常,才會調用這些重載版本的operator delete。
也就是說重載的placement operator delete是用來釋放未能成功創建的對象所占的內存。(正如我們所知,創建一個對象實際上是先申請空間,再調用構造函數。空間申請到了,但是對象卻沒構造出來,那么理所當然需要將空間釋放)
對應上面的四種版本的delete:
//【1】一般的 operator delete()的重載
void operator delete(void*,size_t)
{cout << "operator delete(void*,size_t)" << endl;
}
//【2】對應第二種
void operator delete(void*,void*)
{cout << "operator delete(void*,void*)" << endl;
}
//【3】對應第三種
void operator delete(void*,long)
{cout << "operator delete(void*,long)" << endl;
}
//【4】對應第四種
void operator delete(void*,long,char)
{cout << "operator delete(void*,long,char)" << endl;
}
侯捷老師給出了下面的示例,運行到第五種。我們可以發現,此時的構造函數調用的是第二種構造函數。在之前的定義中,我們在這里拋出了異常。
接下倆便是這幾條語句的執行結果:
如上所示,這些new都被重載了。所以才會打印信息。
按照道理,在構造函數拋出異常后,會調用自己重載的placement delete,打印信息。但在這里并沒有,這是編譯器的原因。
關于basic_string重載new()來擴充申請量
basic_string是標準庫里面的一個class,就是我們使用的字符串。
如下:
template<...>
class basic_string
{
private:struct Rep {...};...void release() {if(--ref == 0) delete this;}inline static void* operator new(size_t,size_t);inline static void operator delete(void*);inline static Rep* create(size_t);...
};
operator new的具體代碼如下:
template<class charT,class traits, class Allocator>
inline void* basic_string<charT,traits,Allocator>::Rep::
operator new(size_t s,size_t extra)
{return Allocator::allocate(s + extra * sizeof(charT));
}
如何使用看這兒:
這里我們把第二參數叫做extra。它的作用是,當使用者去創建一個字符串,如"hello",加上結束符一共6個字符。但是它在分配的時候還會分配extra個字符大小的空間。具體原因不做細究。
template<class charT,class traits,class Allocator>
inline basic_string<charT,traits,Allocator>::Rep*
basic_string<charT,traits,Allocator>::Rep::
create(size_t extra)
{extra = frob_size(extra + 1);Rep *p = new(extra)Rep;...return p;
}
它的placement delete重載之后則長這樣:
template<class charT,class traits,class Allocator>
inline void basic_string<charT,traits,Allocator>::Rep::
operator delete(void* ptr)
{Allocator::deallocate(ptr,sizeof(Rep) + reinterpret_cast<Rep*>(ptr)->res * sizeof(charT));
}