1. new運算符和operator new()
new: 指我們在C++裏通常用到的關鍵字。比如 A *a=new A,爲了實現這一目的,應用程序需要重載new運算符和delete運算符以控制內存分配的過程。對於new來說,有new和::new之分,前者位於std命名空間中
operator new(或operator new[]):是一個標準庫函數,並不是運算符。對於operator new 來說,分爲全局重載和類重載,全局重載是void* ::operator new(size_t size),在類中重載形式void* A::operator new(size_t size)。還要注意的是這裏的operator new()完成的操作一般只是分配內存,事實上系統默認的全局重載也只是調用malloc分配內存,並且返回一個void* 指針。
2.new 和 operator new之間的聯繫
(1)A* a=new A ; 這行代碼實際執行三步操作:
1)new表達式調用一個名爲operator new(或者operator new[])的標準庫函數。該函數分配一塊足夠大的、原始的、未命名的內存空間以便存儲A類型的對象(或者對象數組);
2)編譯器運行相應地構造函數以構造這些對象,併爲其傳入初始值;
3)對象被分配了空間並構造完成,返回一個指向該對象的指針
事實上,如果類A重載了operator new,那麼將調用A::operator new(size_t size),如果沒有重載,就調用全局函數::operator new(size_t size),全局new操作符由c++默認提供。
(2)operator new接口和operator delete接口
標準庫定義了以下8個版本。其中前4個版本可能拋出bad_alloc異常,後4個版本不會拋出異常;
//這些版本可能拋出異常
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;
對於operator new函數:要實現不同的內存分配行爲,應該重載operator new,而不是new。
operator new就像operator + 一樣,是可以重載的。如果類中沒有重載operator new,那麼調用的就是全局的::operator new 來完成堆的內存申請。同理,operator new[]、operator delete、operator delete[]也是可以重載的。
儘管operator new函數和operator delete函數一般用於new表達式,然而它們畢竟是標準庫的兩個普通函數,因此普通的代碼也可以直接調用它們。
3. ::new與new
默認情況下編譯器會將new這個關鍵字翻譯成全局::operator new和相應的構造函數。
但在有的情況下,用戶自己會在類中重載operator new。這種情況下,編譯器默認會使用類中重載的operator new(本質上因爲編譯器會從命名空間由內向外查找自己想要的函數,選用第一個)。
如果我們想要繼續使用默認的operator new,就應該寫成::new ,字面意思是調用最外層命名空間中的operator new。
值得一提的是,全局operator new也是可以被重載的。通過這種方式,我們可以改變所有new的部分行爲。
4. placemen new 定位new表達式
功能:
定位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必須是一個指針,同時在initializers中提供了一個(可能爲空的)以逗號分隔的初始值列表,該初始值列表將用於構造新分配的對象。
1)當僅通過一個地址值調用時,定位new使用operator new (size_t, void*)“分配”它的內存。這是一個我們無法重載的operator new版本。該函數不分配任何內存,它只是簡單地返回指針實參;然後由new表達式負責在指定的地址初始化對象以完成整個工作。
eg. char* ptr = new char[ sizeof(T) ]; //allocate memory
T* tptr = new(ptr) T; //construct in allocated storage ("place")
tptr->~T(); //destruct
delete[] ptr; //deallocate memory
2) 如果不是1)情況,則表達式返回place_address地址。
(C++ Primer 第五版 P753)
eg. Token &Token::operator=( const std::string &s)
{
if ( tok ==STR) //如果當前存儲的是string,可以直接賦值
sval = s;
else
new(&sval) string(s); //否則需要先構造一個string
tok = STR; //更新判別式
return *this;
}
可以看出,通過定位new表達式,我們可以實現保存一塊內存,反覆構造析構,這樣可以省略中間的多次分配內存。由於malloc內存會導致系統調用,這可以節省大量的系統開銷。