目錄
- 一、對new的理解
- 1、new做了什么
- 2、new被編譯器轉為了什么
- 3、operate_new源代碼長啥樣
- 二、對delete的理解
- 1、delete做了什么
- 2、delete被編譯器轉為了什么
- 3、operator delete源代碼長啥樣
- 三、構造函數與析構函數的直接調用
- 參考
一、對new的理解
1、new做了什么
C++告訴我們,new的時候會分配一塊內存用來放對象,分配好之后會調用構造函數。所以說所謂的自動調用,其實是被new調用的。
所以總結new做的動作:1、分配內存 2、調用構造函數
2、new被編譯器轉為了什么
以下面語句為例:
Complex* pc = new Complex(1,2);
仿造編譯器的流程,可以具化為:
try{//1、allocatevoid* mem = operate_new(sizeof(Complex));//2、cast,將指針轉型為Complex*類型指針pc = static_cast<Complex*>(mem);//3、construct,注意這種寫法只有編譯器才能使用,我們需要避免這種行為pc->Complex::Complex(1,2);
}
catch(std::bad_alloc){//若allocation失敗就不執行構造函數
}
注意第3點,如果想要直接調用ctor,可以使用placement new:
new(p)Complex(1,2);
3、operate_new源代碼長啥樣
這里截取的是vc98版本的源代碼:
可見,如果分配內存成功,就返回分配到的內存的指針,否則陷入while循環中。
什么時候會失敗?大量耗用內存之后,我們需要new一個對象,會發現我們調用不到內存了。
這時會使用callnewh函數,即調用newhandle函數,這是一種自設定的函數。也就是說,分配內存失敗就會調用你設定的那個函數。我們需要在newhandle函數中釋放內存,以便調用完newhandle函數后會有內存給malloc分配。
關于函數的第二個參數
nothrow與異常的拋出有關,它是不拋異常,意思是說operate_new這個函數是保證不拋異常的,在新版的C++特性中,不拋異常的寫法有做修改。
它的解釋如下:
struct std::nothrow_t {};
The struct is used as a function parameter to operator new to indicate that the function should return a null pointer to report an
allocation failure, rather than throw an exception.
二、對delete的理解
1、delete做了什么
C++告訴我們,delete的時候會先調用析構函數,然后調用delete函數釋放內存。
2、delete被編譯器轉為了什么
先調用delete函數:
Complex* pc = new Complex(1,2);
...
delete pc;
被編譯器轉為:
pc->~Complex(); //先析構,注意這里可以直接調用析構函數
operator delete(pc); //然后釋放內存
3、operator delete源代碼長啥樣
也就是直接調用free函數。
總結一下,new與delete調用的是operate_new和operator delete。而operate_new調用的是malloc函數,operator delete調用的是free函數。
三、構造函數與析構函數的直接調用
先通過指針調用構造函數,這里先選擇string類,因為string在標準庫里面是個typedefine,本名為basic_string。編譯器把第一個string換成了basic_string,后面再找string沒有找到,所以這里會報錯。這個并不是真正不能使用構造函數的原因。
例1:
string* pstr = new string;cout << "str= " << *pstr << endl;//! pstr->string::string("jjhou"); //[Error] 'class std::basic_string<char>' has no member named 'string'
//! pstr->~string(); //crash -- 其語法語意都是正確的, crash 只因為上一行被 remark 起來嘛. cout << "str= " << *pstr << endl;
例2:
可以看到在GCC里面通過指針或者直接調用構造函數是不正確的,在VC中,條件會放寬。
class A
{
public:int id;A() : id(0) { cout << "default ctor. this=" << this << " id=" << id << endl; }A(int i) : id(i) { cout << "ctor. this=" << this << " id=" << id << endl; }~A() { cout << "dtor. this=" << this << " id=" << id << endl; }
};A* pA = new A(1); //ctor. this=000307A8 id=1cout << pA->id << endl; //1
//! pA->A::A(3); //in VC6 : ctor. this=000307A8 id=3//in GCC : [Error] cannot call constructor 'jj02::A::A' directly//! A::A(5); //in VC6 : ctor. this=0013FF60 id=5// dtor. this=0013FF60 //in GCC : [Error] cannot call constructor 'jj02::A::A' directly// [Note] for a function-style cast, remove the redundant '::A'cout << pA->id << endl; //in VC6 : 3//in GCC : 1 delete pA; //dtor. this=000307A8
參考
https://www.bilibili.com/video/BV1Kb411B7N8?p=7