new operator
內置的new操作符,經常使用的T *ptr = new T(),分配內存,調用構造函數
- 調用operator new分配內存,operator new (sizeof(A))
- 調用構造函數生成類對象,A::A() ,調用placement new
- 返回相應指針
事實上,分配內存這一操作就是由operator new(size_t)來完成的,如果類A重載了operator new,那么將調用A::operator new(size_t ),否則調用全局::operator new(size_t ),后者由C++默認提供。
operator new
像普通運算符一樣可以被重載,operator new會去申請內存,申請失敗的時候會調用new_handler處理,這是一個循環的過程,如果new_handler不拋出異常,會一直循環申請內存,直到成功。
(1) void* operator new (std::size_t size); (2) void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) noexcept; (3) void* operator new (std::size_t size, void* ptr) noexcept;
- 分配size字節的存儲空間,如果成功的話返回一個非空指針,將對象類型進行內存對齊,指向分配空間第一個字節。如果失敗的話,會拋出bad_alloc異常,不調用構造函數
- 和第一種一樣,差別在於,如果失敗的話,不拋出異常,而是返回一個null指針,不調用構造函數
- 只是返回ptr指針,並不分配內存空間。這里的ptr應該指向先前已經分配好的空間,這里的new調用對象的構造函數,在ptr指向的內存空間構造對象或對象數組。ptr指向的內存只要不釋放,可以重復使用,所以這種用法一般在對象池或內存池實現中使用也就是placement new版本
#include <iostream> #include <new> using namespace std; struct A { A( bool xpt ) { if( xpt ) throw( xpt ); } void *operator new(size_t size) { cout<<"operator new"<<endl; return malloc(size); } void operator delete(void *p) { cout<<"operator delete"<<endl; free(p); } void *operator new(size_t size,const nothrow_t &thorw_value) noexcept { cout<<"operator new noexcept"<<endl; return malloc(size); } void operator delete(void *p,const nothrow_t &nowthrow_value) throw() { cout<<"operator delete noexpect."<<endl; free(p); } }; int main() { A* a1 = new A(false); delete a1; try { A* a2 = new A(true); delete a2; } catch( ... ) { // } A* a3 = new(nothrow) A(false); delete a3; try { A* a4 = new(nothrow) A(true); delete a4; } catch( ... ) { } return 0; }
在分配失敗的情況下,拋出異常std::bad_alloc而不是返回NULL,因此通過判斷返回值是否為NULL是徒勞的。
placement new
除了應該有的size_t size參數,多其他的任何參數都可以看做placement new。
- 這種new允許在一塊已經分配成功的內存上重新構造對象或對象數組。placement new不用擔心內存分配失敗,因為它根本不分配內存,它做的唯一一件事情就是調用對象的構造函數
- 用定位放置new操作,既可以在棧(stack)上生成對象,也可以在堆(heap)上生成對象。
- 使用語句A* p=new (mem) A;定位生成對象時,指針p和數組名mem指向同一片存儲區。 會自動調用類A的構造函數,但是由於對象的空間不會自動釋放(對象實際上是借用別人的空間),所以必須顯示的調用類的析構函數,如本例中的p->~A()。
void* operator new (std::size_t size, void* ptr) noexcept;
placement new構造起來的對象或其數組,要顯示的調用他們的析構函數來銷毀,千萬不要使用delete ,要顯式調用它們的析構函數來銷毀(析構函數並不釋放對象的內存,這是因為placement new構造起來的對象或數組大小並不一定等於原來分配的內存大小,使用delete會造成內存泄漏或者之后釋放內存時出現運行時錯誤。
#include <iostream> #include <new> #include <cstdio> using namespace std; struct A { char c; int i; short a; }; int main() { A *a1=new A; A *a2=new(a1) A;//再堆上構造對象 char *c={"fdsafk"};//在棧上構造對象 cout<<(void *)c<<endl; A *a3=new(c) A; cout<<a3<<endl; return 0; }
當使用new運算符定義一個多維數組變量或數組對象時,它產生一個指向數組第一個元素的指針,返回的類型保持了除最左邊維數外的所有維數。例如:
int *p1 = new int[10];
返回的是一個指向int的指針int*
int (*p2)[10] = new int[2][10];
new了一個二維數組, 去掉最左邊那一維[2], 剩下int[10], 所以返回的是一個指向int[10]這種一維數組的指針int (*)[10].
int (*p3)[2][10] = new int[5][2][10]; new了一個三維數組, 去掉最左邊那一維[5], 還有int[2][10], 所以返回的是一個指向二維數組int[2][10]這種類型的指針int (*)[2][10].
#include<iostream> #include <typeinfo> using namespace std; int main() { int *a = new int[34]; int *b = new int[]; int (*c)[2]=new int[][2];//指針 int[34][2]; int (*d)[2] = new int[][2]; //指針 int (*e)[2][3] = new int[34][2][3]; int (*f)[2][3] = new int[][2][3];//指針 a[0] = 1; b[0] = 1; //運行時錯誤,無分配的內存,b只起指針的作用,用來指向相應的數據 c[0][0] = 1; d[0][0] = 1;//運行時錯誤,無分配的內存,d只起指針的作用,用來指向相應的數據 e[0][0][0] = 1; f[0][0][0] = 1;//運行時錯誤,無分配的內存,f只起指針的作用,用來指向相應的數據 cout<<typeid(a).name()<<endl; cout<<typeid(b).name()<<endl; cout<<typeid(c).name()<<endl; cout<<typeid(d).name()<<endl; cout<<typeid(e).name()<<endl; cout<<typeid(f).name()<<endl; delete[] a; delete[] b; delete[] c; delete[] d; delete[] e; delete[] f; return 0; }