(Boolan)詳解 C++ Operator new\delete、placement new\delete厦画、Array new\delete

題目內容:

class Fruit{
int no;
double weight;
char key;
public:
void print() { }
virtual void process(){ }
};

class Apple: public Fruit{
int size;
char type;
public:
void save() { }
virtual void process(){ }
};

>
>
>為上周題目中的 Fruit和Apple 添加 構造函數與 析構函數牍陌, 并在構造函數與析構函數中打印控制臺信息擎浴,
觀察構造和析枸調用過程。然后為Apple類重載::operator new和 ::operator delete毒涧,在控制臺打印信息贮预,并觀察調用結果。

#答案:
---
## 為了文章結構契讲,我把代碼放在文章后面仿吞,上面寫的為代碼片段,但為了方便查看運行結果捡偏,可以點擊括號里面的鏈接(http://rextester.com/DOLN3287 )唤冈,進入后點擊"run it"按鈕就可以查看結果了
---
![父類和子類的關系](http://upload-images.jianshu.io/upload_images/5688965-16eec8112ecf6fa8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#### 1.子類對象創(chuàng)建時:會先創(chuàng)建父類對象,再創(chuàng)建子類對象(之前講的裝快遞的故事)
#### 2.子-類對象銷毀時:會先銷毀子類對象银伟,再銷毀父類對象(之前講的拆快遞的故事)
#### 3.子類的大小 = 父類成員屬性的大小 + 虛函數指針(不存在為0Byte) + 子類成員屬性的大小
#### 4.子類的內存中包括父類對象(由內存地址相同可知)
#### 5.placement new和placement delete最好能成對出現你虹,除非保證創(chuàng)建對象時不會產生異常
#### 6.placement delete只會在new對象時拋出異常時才會調用绘搞,并且是成對調用(形參列表相同)。
#### 7.placement new創(chuàng)建對象如果不拋出異常傅物,不會調用placement delete夯辖,而是調用operator new。
#### 8.不成對的placement new和placement delete的時候董饰,如果產生了異常楼雹,則無法已經申請的釋放空間,會造成內存泄漏尖阔。
---
## 為了文章結構贮缅,我把代碼放在文章后面,上面寫的為代碼片段介却,但為了方便查看運行結果谴供,可以點擊括號里面的鏈接(http://rextester.com/DOLN3287 ),進入后點擊"run it"按鈕就可以查看結果了
---
- Fruit和Apple的UML關系圖
![UML](http://upload-images.jianshu.io/upload_images/5688965-e9b8100020917b94.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

### 測試1:*** 子父類的構造函數齿坷、析構函數的調用情況 ***
- Fruit構造函數(節(jié)選)

.....
class Fruit {
int no;
double weight;
char key;
public:
Fruit(int n = 0, double w = 0., char k = '0');//Fruit類構造函數
~Fruit();//Fruit的析構函數
....
void print();
};
....
using namespace std;
inline Fruit::Fruit(int n, double w, char k) :no(n), weight(w), key(k) {
//如果為無參調用桂肌,隨機生成Fruit中間的參數
if (n == 0 && w == 0. && k == '0') {
no = rand() % 100;
weight = rand() / RAND_MAX;
key = rand() % 26 + 97;
}
//Fruit的構造函數調用打印
printCalled("Fruit", "Fruit(int, double, char)");
}
.....
inline Fruit::~Fruit() {
//析構函數調用時,輸出結果
cout << dec << "\nThe object Fruit(" << no << ", " << weight << ", " << key << ") has been deconstructed" << endl;
}
.....
inline void Fruit::print() {
//對象內容打印
cout << dec
<< "Fruit(" << no << ", " << weight << ", " << key << ")\n"
<< "size = " << sizeof(*this) << ", " << "address = 0x" << hex << this << endl;
}
.....


- Apple構造函數(節(jié)選)

class Apple : public Fruit {
private:
int size;
char type;
public:
Apple(int n = 0, double w = 0, char k = '0', int s = 0, char t = '0'); //構造函數
~Apple(); //析構函數
void print();
........
};
inline Apple::Apple(int n, double w, char k, int s, char t) :Fruit(n, w, k), size(s), type(t) {
//無參調用Apple的構造函數永淌,會隨機生成內容
if (n == 0 && w == 0. && k == '0' && s == 0 && t == '0') {
size = rand() % 100;
type = rand() % 26 + 97;
}
//構造函數被調用崎场,打印信息
printCalled("Apple", "Apple(int, double, char, int, char)");
//打印對象信息
print();
}
....
inline Apple::~Apple() {
//析構函數
printCalled("Apple", "~Apple()");
}
......
inline void Apple::print() {
//打印Apple的對象信息
Fruit::print();
cout << dec << "\nApple (" << size << ", " << type << ")\n"
<< "size = " << sizeof(*this) << ", " << "address = 0x" << hex << this << endl;
}


- 測試部分代碼

.....
inline void printCalled(const string scope, const string functionName) {
cout << "\n(" << scope << ") "<< functionName << " has been called\n";
}
const string s = "\n\n" + x * 50 + "\n\n";
int mian(){
.....
srand((unsigned) time(NULL));
//在棧里面創(chuàng)建對象的測試
cout << s << "\n" "在棧里面創(chuàng)建對象的測試" << endl;
Apple();//臨時對象,生命周期僅在這一句遂蛀,執(zhí)行完就會彈出棧
.....
}
.....


- 結果
![在棧中創(chuàng)建對象](http://upload-images.jianshu.io/upload_images/5688965-8daca29432f24580.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 結論
1.子類對象創(chuàng)建時:會先創(chuàng)建父類對象谭跨,再創(chuàng)建子類對象(之前講的裝快遞的故事)
2.子-類對象銷毀時:會先銷毀子類對象,再銷毀父類對象(之前講的拆快遞的故事)
3.子類的大小 = 父類成員屬性的大小 + 虛函數指針(不存在為0) + 子類成員屬性的大小
4.子類的內存中包括父類對象(由內存地址相同可知)
---
### 測試二:測試operator new(無異常)
- Fruit代碼

class Fruit {
int no;
double weight;
char key;
public:
Fruit(int n = 0, double w = 0., char k = '0');//Fruit類構造函數
~Fruit();//Fruit的析構函數

void* operator new(size_t size);    //覆寫的Fruit的operator new

void operator delete(void* p);  //復寫Fruit的operator delete
void operator delete(void* p, int type);

virtual void process() {   }
void print();

};
using namespace std;
inline Fruit::Fruit(int n, double w, char k) :no(n), weight(w), key(k) {
//如果為無參調用李滴,隨機生成Fruit中間的參數
if (n == 0 && w == 0. && k == '0') {
no = rand() % 100;
weight = rand() / RAND_MAX;
key = rand() % 26 + 97;
}
//Fruit的構造函數調用打印
printCalled("Fruit", "Fruit(int, double, char)");

//打印Fruit的對象情況
print();

}
inline Fruit::~Fruit() {
//析構函數調用時螃宙,輸出結果
cout << dec << "\nThe object Fruit(" << no << ", " << weight << ", " << key << ") has been deconstructed" << endl;
}
inline void Fruit::print() {
//對象內容打印
cout << dec
<< "Fruit(" << no << ", " << weight << ", " << key << ")\n"
<< "size = " << sizeof(this) << ", " << "address = 0x" << hex << this << endl;
}
inline void
Fruit::operator new(size_t size) {
//operator new
cout << "size_t = " << size << endl;
printCalled("Fruit", "void* operator new(size_t)");
//this->print(); 錯誤提示:static不能調用non-static
//如果使用this指針在該處調用,會報錯所坯,operator new會自動轉換為static的函數谆扎,僅用于分配空間,不會修改對象的內容
//換而言之芹助,此時對象還尚未創(chuàng)建完成堂湖,僅僅分配了內存空間,所以this指針存在也是不可能的
return malloc(size);
}
inline void Fruit::operator delete(void* p) {
//operator delete被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void)");
//釋放內存
free(p);
}
inline void Fruit::operator delete(void
p, int type) {
//placement delete 被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void*, int)");
//釋放內存
free(p);
}


- Apple代碼

class Apple : public Fruit {
private:
int size;
char type;
public:
Apple(int n = 0, double w = 0, char k = '0', int s = 0, char t = '0'); //構造函數
....
~Apple(); //析構函數
void* operator new(size_t size); //覆寫的Apple的operator new
void operator delete(void* p); //覆寫的Apple的operator delete

void print();
....

};
inline Apple::Apple(int n, double w, char k, int s, char t) :Fruit(n, w, k), size(s), type(t) {
//無參調用Apple的構造函數状土,會隨機生成內容
if (n == 0 && w == 0. && k == '0' && s == 0 && t == '0') {
size = rand() % 100;
type = rand() % 26 + 97;
}
//構造函數被調用无蜂,打印信息
printCalled("Apple", "Apple(int, double, char, int, char)");
//打印對象信息
print();
}
inline Apple::~Apple() {
//析構函數
printCalled("Apple", "~Apple()");
}
inline void* Apple::operator new(size_t size) {
//operator new
cout << "size_t = " << size << endl;
printCalled("Apple", "void* operator new(size_t)");
//this->print(); 錯誤提示:static不能調用non-static
//如果使用this指針在該處調用,會報錯声诸,operator new會自動轉換為static的函數酱讶,僅用于分配空間,不會修改對象的內容
//換而言之彼乌,此時對象還尚未創(chuàng)建完成泻肯,僅僅分配了內存空間渊迁,所以this指針存在也是不可能的
return malloc(size);
}
inline void Apple::operator delete(void* p) {
//operator delete
printCalled("Apple", "void operator delete(void*)");
free(p);
}

- 測試代碼

int main()
{
srand((unsigned) time(NULL));
.......

//在堆里面創(chuàng)建對象的測試
cout << s << "\n" "通過普通的new罢荡,在堆里面創(chuàng)建對象的測試" << endl;
Apple* pa = new Apple();
delete pa;

}

- 運行結果
![operator new測試](http://upload-images.jianshu.io/upload_images/5688965-0282d9149974ef38.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 結論 
1.operator new 中的參數std::size_t的實參的大小實際為該對象所需的大小
2.調用子類的operator new / operator delete時座每,并不會調用父類的operate new 
/ operator delete
---
###測試三:operator new創(chuàng)建對象產生異常時的處理
- Fruit代碼:

class Fruit {
int no;
double weight;
char key;
public:
Fruit(int n = 0, double w = 0., char k = '0');//Fruit類構造函數
~Fruit();//Fruit的析構函數
void* operator new(size_t size); //覆寫的Fruit的operator new
void operator delete(void* p); //復寫Fruit的operator delete
void operator delete(void* p, int type);
virtual void process() { }
void print();
};
using namespace std;
inline Fruit::Fruit(int n, double w, char k) :no(n), weight(w), key(k) {
//如果為無參調用,隨機生成Fruit中間的參數
if (n == 0 && w == 0. && k == '0') {
no = rand() % 100;
weight = rand() / RAND_MAX;
key = rand() % 26 + 97;
}
//Fruit的構造函數調用打印
printCalled("Fruit", "Fruit(int, double, char)");
//打印Fruit的對象情況
print();
}
inline Fruit::~Fruit() {
//析構函數調用時痒谴,輸出結果
cout << dec << "\nThe object Fruit(" << no << ", " << weight << ", " << key << ") has been deconstructed" << endl;
}
inline void Fruit::print() {
//對象內容打印
cout << dec
<< "Fruit(" << no << ", " << weight << ", " << key << ")\n"
<< "size = " << sizeof(this) << ", " << "address = 0x" << hex << this << endl;
}
inline void
Fruit::operator new(size_t size) {
//operator new
cout << "size_t = " << size << endl;
printCalled("Fruit", "void* operator new(size_t)");
//this->print(); 錯誤提示:static不能調用non-static
//如果使用this指針在該處調用稚铣,會報錯箱叁,operator new會自動轉換為static的函數,僅用于分配空間惕医,不會修改對象的內容
//換而言之耕漱,此時對象還尚未創(chuàng)建完成,僅僅分配了內存空間抬伺,所以this指針存在也是不可能的
return malloc(size);
}
inline void Fruit::operator delete(void* p) {
//operator delete被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void)");
//釋放內存
free(p);
}
inline void Fruit::operator delete(void
p, int type) {
//placement delete 被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void*, int)");
//釋放內存
free(p);
}

- Apple代碼:

class Apple : public Fruit {
private:
int size;
char type;
public:
Apple(char test); //會拋出異常的構造函數
~Apple(); //析構函數
void* operator new(size_t size); //覆寫的Apple的operator new
void operator delete(void* p, int type); //覆寫的placement delete
void print();
void save() { }
virtual void process() { }
.......
};
inline Apple::Apple(char test) {
//有參構造函數調用時打印信息
printCalled("Apple", "Apple(int)");
//拋出自定義異常
throw TestException();
}
inline Apple::~Apple() {
//析構函數
printCalled("Apple", "~Apple()");
}
inline void* Apple::operator new(size_t size) {
//operator new
cout << "size_t = " << size << endl;
printCalled("Apple", "void* operator new(size_t)");
//this->print(); 錯誤提示:static不能調用non-static
//如果使用this指針在該處調用螟够,會報錯,operator new會自動轉換為static的函數峡钓,僅用于分配空間妓笙,不會修改對象的內容
//換而言之,此時對象還尚未創(chuàng)建完成能岩,僅僅分配了內存空間寞宫,所以this指針存在也是不可能的
return malloc(size);
}
inline void Apple::operator delete(void* p) {
//operator delete
printCalled("Apple", "void operator delete(void*)");
free(p);
}

- 測試代碼:

int main()
{
.....
Apple* pa1 = NULL;
//測試調用拋出異常的構造函數,重寫了operator new拉鹃,檢測拋出異常后辈赋,會調用哪個delete
cout << s << "\n" "測試調用拋出異常的構造函數,重寫了operator new毛俏,檢測拋出異常后炭庙,會調用哪個delete " << endl;
try {
pa1 = new Apple('1');//
}
catch (TestException &e) {
cout << "\nexception catched\n" << e;
if(pa1)
delete pa1;
}
....
return 0;
}

- 運行結果:

![operator new的異常處理](http://upload-images.jianshu.io/upload_images/5688965-9ab8385849df21cf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 結論:
1.調用operator new創(chuàng)建對象時饲窿,如果創(chuàng)建對象時產生異常煌寇,處理異常時`delete p;`,會調用`operator delete;`來釋放申請的內存逾雄。
2.自定義異常類對象阀溶,會在異常處理完成后被銷毀
---
### 測試四:placement new處理異常時調用情況
- Fruit代碼

class Fruit {
int no;
double weight;
char key;
public:
Fruit(int n = 0, double w = 0., char k = '0');//Fruit類構造函數
~Fruit();//Fruit的析構函數
void* operator new(size_t size); //覆寫的Fruit的operator new
void operator delete(void* p); //復寫Fruit的operator delete
void operator delete(void* p, int type);
virtual void process() { }
void print();
};
using namespace std;
inline Fruit::Fruit(int n, double w, char k) :no(n), weight(w), key(k) {
//如果為無參調用,隨機生成Fruit中間的參數
if (n == 0 && w == 0. && k == '0') {
no = rand() % 100;
weight = rand() / RAND_MAX;
key = rand() % 26 + 97;
}
//Fruit的構造函數調用打印
printCalled("Fruit", "Fruit(int, double, char)");
//打印Fruit的對象情況
print();
}
inline Fruit::~Fruit() {
//析構函數調用時鸦泳,輸出結果
cout << dec << "\nThe object Fruit(" << no << ", " << weight << ", " << key << ") has been deconstructed" << endl;
}
inline void Fruit::print() {
//對象內容打印
cout << dec
<< "Fruit(" << no << ", " << weight << ", " << key << ")\n"
<< "size = " << sizeof(this) << ", " << "address = 0x" << hex << this << endl;
}
inline void
Fruit::operator new(size_t size) {
//operator new
cout << "size_t = " << size << endl;
printCalled("Fruit", "void* operator new(size_t)");
//this->print(); 錯誤提示:static不能調用non-static
//如果使用this指針在該處調用银锻,會報錯,operator new會自動轉換為static的函數做鹰,僅用于分配空間击纬,不會修改對象的內容
//換而言之,此時對象還尚未創(chuàng)建完成钾麸,僅僅分配了內存空間更振,所以this指針存在也是不可能的
return malloc(size);
}
inline void Fruit::operator delete(void* p) {
//operator delete被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void)");
//釋放內存
free(p);
}
inline void Fruit::operator delete(void
p, int type) {
//placement delete 被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void*, int)");
//釋放內存
free(p);
}

- Apple代碼

class Apple : public Fruit {
private:
int size;
char type;
public:
Apple(int n = 0, double w = 0, char k = '0', int s = 0, char t = '0'); //構造函數
Apple(char test); //會拋出異常的構造函數
~Apple(); //析構函數

void* operator new(size_t size, int type);  //覆寫的Apple的第一個placement new
void operator delete(void* p, int type);    //覆寫的placement delete
void print();
void save() {   }
virtual void process() {   }

};
inline Apple::Apple(char test) {
//有參構造函數調用時打印信息
printCalled("Apple", "Apple(int)");
//拋出自定義異常
throw TestException();
}
inline Apple::~Apple() {
//析構函數
printCalled("Apple", "~Apple()");
}
inline void* Apple::operator new(size_t size, int type) {
//placement new
printCalled("Apple placement new <int>", "void* operator new(size_t, int)");
return malloc(size);
}
inline void Apple::operator delete(void* p, int type) {
//placement delete
//為和此處不可以使用this指針來釋放內存空間呢炕桨?
//this->print();嘗試
//如果使用this指針在該出調用,會報錯肯腕,operator delete會自動轉換為static的函數献宫,所以形參列表中必須傳入一個指針
printCalled("Apple placement delete <int>", "void operator delete(void, int)");
free(p);
}
inline void Apple::print() {
//打印Apple的對象信息
Fruit::print();
cout << dec << "\nApple (" << size << ", " << type << ")\n"
<< "size = " << sizeof(
this) << ", " << "address = 0x" << hex << this << endl;
}

- 測試代碼

int main()
{
.....
Apple* pa2 = NULL;
//測試拋出異常的構造函數,重寫并調用了placement new,檢測拋出異常后实撒,會調用哪個delete
cout << s << "\n" "測試拋出異常的構造函數姊途,重寫并調用了placement new,檢測拋出異常后,會調用哪個delete" << endl;
try {
pa2 = new (1)Apple('1'); //調用了void* Apple::operator new(size_t, int)
}
catch (TestException &e) {
cout << "\nexception catched\n" << e;
if (pa2)
delete pa2;
}
....
return 0;
}

- 運行結果
![operator new的異常處理](http://upload-images.jianshu.io/upload_images/5688965-73c36459449b20ce.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 結論
1.new對象的時候知态,如果使用的時候用的是placement new捷兰,則處理異常時,會調用對應形參列表的placement delete
---
### 測試五:placement new 創(chuàng)建對象時负敏,不產生異常的情況
- Fruit代碼

class Fruit {
int no;
double weight;
char key;
public:
Fruit(int n = 0, double w = 0., char k = '0');//Fruit類構造函數
~Fruit();//Fruit的析構函數
void* operator new(size_t size); //覆寫的Fruit的operator new
void operator delete(void* p); //復寫Fruit的operator delete
void operator delete(void* p, int type);
virtual void process() { }
void print();
};
using namespace std;
inline Fruit::Fruit(int n, double w, char k) :no(n), weight(w), key(k) {
//如果為無參調用寂殉,隨機生成Fruit中間的參數
if (n == 0 && w == 0. && k == '0') {
no = rand() % 100;
weight = rand() / RAND_MAX;
key = rand() % 26 + 97;
}
//Fruit的構造函數調用打印
printCalled("Fruit", "Fruit(int, double, char)");
//打印Fruit的對象情況
print();
}
inline Fruit::~Fruit() {
//析構函數調用時,輸出結果
cout << dec << "\nThe object Fruit(" << no << ", " << weight << ", " << key << ") has been deconstructed" << endl;
}
inline void Fruit::print() {
//對象內容打印
cout << dec
<< "Fruit(" << no << ", " << weight << ", " << key << ")\n"
<< "size = " << sizeof(this) << ", " << "address = 0x" << hex << this << endl;
}
inline void
Fruit::operator new(size_t size) {
//operator new
cout << "size_t = " << size << endl;
printCalled("Fruit", "void* operator new(size_t)");
//this->print(); 錯誤提示:static不能調用non-static
//如果使用this指針在該處調用原在,會報錯友扰,operator new會自動轉換為static的函數,僅用于分配空間庶柿,不會修改對象的內容
//換而言之村怪,此時對象還尚未創(chuàng)建完成,僅僅分配了內存空間浮庐,所以this指針存在也是不可能的
return malloc(size);
}
inline void Fruit::operator delete(void* p) {
//operator delete被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void)");
//釋放內存
free(p);
}
inline void Fruit::operator delete(void
p, int type) {
//placement delete 被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void*, int)");
//釋放內存
free(p);
}

- Apple代碼

class Apple : public Fruit {
private:
int size;
char type;
public:
Apple(int n = 0, double w = 0, char k = '0', int s = 0, char t = '0'); //構造函數
~Apple(); //析構函數
void* operator new(size_t size); //覆寫的Apple的operator new
void* operator new(size_t size, int type); //覆寫的Apple的第一個placement new

void operator delete(void* p);      //覆寫的Apple的operator delete
void operator delete(void* p, int type);    //覆寫的placement delete
void print();

.....
};
inline Apple::Apple(int n, double w, char k, int s, char t) :Fruit(n, w, k), size(s), type(t) {
//無參調用Apple的構造函數甚负,會隨機生成內容
if (n == 0 && w == 0. && k == '0' && s == 0 && t == '0') {
size = rand() % 100;
type = rand() % 26 + 97;
}
//構造函數被調用,打印信息
printCalled("Apple", "Apple(int, double, char, int, char)");
//打印對象信息
print();
}
inline Apple::~Apple() {
//析構函數
printCalled("Apple", "~Apple()");
}
inline void* Apple::operator new(size_t size) {
//operator new
cout << "size_t = " << size << endl;
printCalled("Apple", "void* operator new(size_t)");
//this->print(); 錯誤提示:static不能調用non-static
//如果使用this指針在該處調用审残,會報錯梭域,operator new會自動轉換為static的函數,僅用于分配空間搅轿,不會修改對象的內容
//換而言之病涨,此時對象還尚未創(chuàng)建完成,僅僅分配了內存空間璧坟,所以this指針存在也是不可能的
return malloc(size);
}
inline void* Apple::operator new(size_t size, int type) {
//placement new
printCalled("Apple placement new <int>", "void* operator new(size_t, int)");
return malloc(size);
}
inline void Apple::operator delete(void* p) {
//operator delete
printCalled("Apple", "void operator delete(void)");
free(p);
}
inline void Apple::operator delete(void
p, int type) {
//placement delete
//為和此處不可以使用this指針來釋放內存空間呢既穆?
//this->print();嘗試
//如果使用this指針在該出調用,會報錯雀鹃,operator delete會自動轉換為static的函數幻工,所以形參列表中必須傳入一個指針
printCalled("Apple placement delete <int>", "void operator delete(void, int)");
free(p);
}
inline void Apple::print() {
//打印Apple的對象信息
Fruit::print();
cout << dec << "\nApple (" << size << ", " << type << ")\n"
<< "size = " << sizeof(
this) << ", " << "address = 0x" << hex << this << endl;
}

- 測試代碼

int main(){
//測試調用正常的構造函數的過程,使用的是重寫的operator new黎茎,檢測會調用那個delete
cout << s << "\n""測試調用正常的構造函數的過程囊颅,使用的是重寫的operator new,檢測會調用那個delete" << endl;
Apple* pa3 = new(1)Apple();
delete pa3;
return 0;
}

- 運行結果
![使用placement new創(chuàng)建對象不產生異常的結果](http://upload-images.jianshu.io/upload_images/5688965-b9f43aff7b4c1435.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 結論
1.調用placement new來創(chuàng)建對象時,如果沒有產生異常踢代,在delete時會調用operator delete
---
### 測試六:placement new 創(chuàng)建對象時先鱼,沒有成對的placement delete的情況
- Fruit代碼

class Fruit {
int no;
double weight;
char key;
public:
Fruit(int n = 0, double w = 0., char k = '0');//Fruit類構造函數
~Fruit();//Fruit的析構函數
void* operator new(size_t size); //覆寫的Fruit的operator new
void operator delete(void* p); //復寫Fruit的operator delete
void operator delete(void* p, int type);
virtual void process() { }
void print();
};
using namespace std;
inline Fruit::Fruit(int n, double w, char k) :no(n), weight(w), key(k) {
//如果為無參調用,隨機生成Fruit中間的參數
if (n == 0 && w == 0. && k == '0') {
no = rand() % 100;
weight = rand() / RAND_MAX;
key = rand() % 26 + 97;
}
//Fruit的構造函數調用打印
printCalled("Fruit", "Fruit(int, double, char)");
//打印Fruit的對象情況
print();
}
inline Fruit::~Fruit() {
//析構函數調用時奸鬓,輸出結果
cout << dec << "\nThe object Fruit(" << no << ", " << weight << ", " << key << ") has been deconstructed" << endl;
}
inline void Fruit::print() {
//對象內容打印
cout << dec
<< "Fruit(" << no << ", " << weight << ", " << key << ")\n"
<< "size = " << sizeof(this) << ", " << "address = 0x" << hex << this << endl;
}
inline void
Fruit::operator new(size_t size) {
//operator new
cout << "size_t = " << size << endl;
printCalled("Fruit", "void* operator new(size_t)");
//this->print(); 錯誤提示:static不能調用non-static
//如果使用this指針在該處調用焙畔,會報錯,operator new會自動轉換為static的函數串远,僅用于分配空間宏多,不會修改對象的內容
//換而言之,此時對象還尚未創(chuàng)建完成澡罚,僅僅分配了內存空間伸但,所以this指針存在也是不可能的
return malloc(size);
}
inline void Fruit::operator delete(void* p) {
//operator delete被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void)");
//釋放內存
free(p);
}
inline void Fruit::operator delete(void
p, int type) {
//placement delete 被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void*, int)");
//釋放內存
free(p);
}

- Apple代碼

class Apple : public Fruit {
private:
int size;
char type;
public:
Apple(char test); //會拋出異常的構造函數
~Apple(); //析構函數
void* operator new(size_t size, int type); //覆寫的Apple的第一個placement new
void* operator new(size_t size, char type); //覆寫的Apple的第二個placement new(這個一個沒有配對的placement delete,為了方便測試內存泄漏情況)
void operator delete(void* p); //覆寫的Apple的operator delete
void operator delete(void* p, int type); //覆寫的placement delete
void print();
};
inline Apple::Apple(char test) {
//有參構造函數調用時打印信息
printCalled("Apple", "Apple(int)");
//拋出自定義異常
throw TestException();
}
inline Apple::~Apple() {
//析構函數
printCalled("Apple", "~Apple()");
}
inline void* Apple::operator new(size_t size, char type) {
//placement new
cout << "size_t = " << size << endl;
printCalled("Apple placement new <char>", "void* operator new(size_t, char)");
return malloc(size);
}
inline void Apple::operator delete(void* p) {
//operator delete
printCalled("Apple", "void operator delete(void)");
free(p);
}
inline void Apple::operator delete(void
p, int type) {
//placement delete
//為和此處不可以使用this指針來釋放內存空間呢留搔?
//this->print();嘗試
//如果使用this指針在該出調用更胖,會報錯,operator delete會自動轉換為static的函數隔显,所以形參列表中必須傳入一個指針
printCalled("Apple placement delete <int>", "void operator delete(void, int)");
free(p);
}
inline void Apple::print() {
//打印Apple的對象信息
Fruit::print();
cout << dec << "\nApple (" << size << ", " << type << ")\n"
<< "size = " << sizeof(
this) << ", " << "address = 0x" << hex << this << endl;
}

- 測試代碼

int main(){

return 0;

}

- 運行結果
![不成對的placement new被調用還產生了異常](http://upload-images.jianshu.io/upload_images/5688965-7107f58840132473.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 結論
1.如果不存在成對的的placement new和placement delete却妨,那么必須保證,** 創(chuàng)建對象時不會產生異常 **括眠,否則就會造成內存泄漏彪标。***如果存在產生異常的可能性,placement new和placement delete必須要成對出現掷豺。***
---
### 測試六:Array new測試
- Fruit代碼

class Fruit {
int no;
double weight;
char key;
public:
Fruit(int n = 0, double w = 0., char k = '0');//Fruit類構造函數
~Fruit();//Fruit的析構函數
void* operator new(size_t size); //覆寫的Fruit的operator new
void operator delete(void* p); //復寫Fruit的operator delete
void operator delete(void* p, int type);
virtual void process() { }
void print();
};
using namespace std;
inline Fruit::Fruit(int n, double w, char k) :no(n), weight(w), key(k) {
//如果為無參調用捞烟,隨機生成Fruit中間的參數
if (n == 0 && w == 0. && k == '0') {
no = rand() % 100;
weight = rand() / RAND_MAX;
key = rand() % 26 + 97;
}
//Fruit的構造函數調用打印
printCalled("Fruit", "Fruit(int, double, char)");
//打印Fruit的對象情況
print();
}
inline Fruit::~Fruit() {
//析構函數調用時,輸出結果
cout << dec << "\nThe object Fruit(" << no << ", " << weight << ", " << key << ") has been deconstructed" << endl;
}
inline void Fruit::print() {
//對象內容打印
cout << dec
<< "Fruit(" << no << ", " << weight << ", " << key << ")\n"
<< "size = " << sizeof(this) << ", " << "address = 0x" << hex << this << endl;
}
inline void
Fruit::operator new(size_t size) {
//operator new
cout << "size_t = " << size << endl;
printCalled("Fruit", "void* operator new(size_t)");
//this->print(); 錯誤提示:static不能調用non-static
//如果使用this指針在該處調用当船,會報錯题画,operator new會自動轉換為static的函數,僅用于分配空間德频,不會修改對象的內容
//換而言之苍息,此時對象還尚未創(chuàng)建完成,僅僅分配了內存空間抱婉,所以this指針存在也是不可能的
return malloc(size);
}
inline void Fruit::operator delete(void* p) {
//operator delete被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void)");
//釋放內存
free(p);
}
inline void Fruit::operator delete(void
p, int type) {
//placement delete 被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void*, int)");
//釋放內存
free(p);
}

- Apple代碼

class Apple : public Fruit {
private:
int size;
char type;
public:
Apple(int n = 0, double w = 0, char k = '0', int s = 0, char t = '0'); //構造函數
Apple(char test); //會拋出異常的構造函數
~Apple(); //析構函數
void* operator new[](size_t size); //覆寫的Apple的Array new
void operator delete[](void* p); //覆寫的Array delete
void print();
.....
};
inline Apple::Apple(int n, double w, char k, int s, char t) :Fruit(n, w, k), size(s), type(t) {
//無參調用Apple的構造函數档叔,會隨機生成內容
if (n == 0 && w == 0. && k == '0' && s == 0 && t == '0') {
size = rand() % 100;
type = rand() % 26 + 97;
}
//構造函數被調用,打印信息
printCalled("Apple", "Apple(int, double, char, int, char)");
//打印對象信息
print();
}
inline Apple::~Apple() {
//析構函數
printCalled("Apple", "~Apple()");
}
inline void* Apple::operator new[](size_t size) {
//operator new[]
printCalled("Apple", "void* operator new(size_t)");
return malloc(size);
}
inline void Apple::operator delete[](void* p) {
//array delete
printCalled("Apple", "void operator delete");
free(p);
}
inline void Apple::print() {
//打印Apple的對象信息
Fruit::print();
cout << dec << "\nApple (" << size << ", " << type << ")\n"
<< "size = " << sizeof(*this) << ", " << "address = 0x" << hex << this << endl;
}

- 測試代碼

int main(){
//數組測試
cout << s << "\n" "數組測試" << endl;
Apple* as1 = new Apple[2];
delete[] as1;
return 0;
}

- 運行結果
![Array new創(chuàng)建和銷毀的情況](http://upload-images.jianshu.io/upload_images/5688965-39b53519113bacc2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 結論
1.對于數組創(chuàng)建蒸绩,使用array new,會調用構造函數N次
2.對于數組的銷毀铃肯,使用array delete患亿,會調用其中對象的析構函數N次
---
- 全部代碼

//************************
//************************
//*******自定義異常類******
//************************
//************************

ifndef TEST_EXCEPTION___

define TEST_EXCEPTION___

include <iostream>

using namespace std;

//自定義異常
class TestException{
public:
//異常構造函數
TestException() {
cout << "TestException() has been called" << endl;
}
//異常的析構函數
~TestException() {
cout << "~TestException() has been called" << endl;
}

};

//打印異常信息
inline ostream& operator<< (ostream &o, const TestException &e) {
o << "TestException\n";
return o;
}

endif // !TEST_EXCEPTION___

//************************
//************************
//*******Fruit 和Apple****
//************************
//************************

ifndef FRUIT__EL_

define FRUIT__EL_

include <iostream>

//#include "TestException.h"

include <string>

include <iomanip>

include <ctime>

include <cstdlib>

string operator(string, const int);
inline void printCalled(const string, const string);
extern const string x = "
";

class Fruit {
int no;
double weight;
char key;
public:
Fruit(int n = 0, double w = 0., char k = '0');//Fruit類構造函數
~Fruit();//Fruit的析構函數

void* operator new(size_t size);

void operator delete(void* p);  //復寫Fruit的operator delete
void operator delete(void* p, int type);

virtual void process() {   }
void print();

};

class Apple : public Fruit {
private:
int size;
char type;
public:
Apple(int n = 0, double w = 0, char k = '0', int s = 0, char t = '0'); //構造函數
Apple(char test); //會拋出異常的構造函數
~Apple(); //析構函數

void* operator new(size_t size);    //覆寫的Apple的operator new
void* operator new[](size_t size);  //覆寫的Apple的Array new
void* operator new(size_t size, int type);  //覆寫的Apple的第一個placement new
void* operator new(size_t size, char type); //覆寫的Apple的第二個placement new(這個一個沒有配對的placement delete,為了方便測試內存泄漏情況)


void operator delete(void* p);      //覆寫的Apple的operator delete
void operator delete(void* p, int type);    //覆寫的placement delete

void operator delete[](void* p);    //覆寫的Array delete

void print();
void save() {   }
virtual void process() {   }

};

using namespace std;
inline Fruit::Fruit(int n, double w, char k) :no(n), weight(w), key(k) {
//如果為無參調用,隨機生成Fruit中間的參數
if (n == 0 && w == 0. && k == '0') {
no = rand() % 100;
weight = rand() / RAND_MAX;
key = rand() % 26 + 97;
}
//Fruit的構造函數調用打印
printCalled("Fruit", "Fruit(int, double, char)");

}
inline Fruit::~Fruit() {
//析構函數調用時步藕,輸出結果
cout << dec << "\nThe object Fruit(" << no << ", " << weight << ", " << key << ") has been deconstructed" << endl;
}

inline void Fruit::print() {
//對象內容打印
cout << dec
<< "Fruit(" << no << ", " << weight << ", " << key << ")\n"
<< "size = " << sizeof(*this) << ", " << "address = 0x" << hex << this << endl;
}

inline void* Fruit::operator new(size_t size) {
//operator new
cout << "size_t = " << size << endl;
printCalled("Apple", "void* operator new(size_t)");

//this->print();    錯誤提示:static不能調用non-static
//如果使用this指針在該處調用惦界,會報錯,operator new會自動轉換為static的函數咙冗,僅用于分配空間沾歪,不會修改對象的內容
//換而言之,此時對象還尚未創(chuàng)建完成雾消,僅僅分配了內存空間灾搏,所以this指針存在也是不可能的

return malloc(size);

}

inline void Fruit::operator delete(void* p) {
//operator delete被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void*)");
//釋放內存
free(p);
}

inline void Fruit::operator delete(void* p, int type) {
//placement delete 被調用時打印調用信息
printCalled("Fruit", "void* operator delete(void*, int)");
//釋放內存
free(p);
}

inline Apple::Apple(int n, double w, char k, int s, char t) :Fruit(n, w, k), size(s), type(t) {
//無參調用Apple的構造函數,會隨機生成內容
if (n == 0 && w == 0. && k == '0' && s == 0 && t == '0') {
size = rand() % 100;
type = rand() % 26 + 97;
}
//構造函數被調用立润,打印信息
printCalled("Apple", "Apple(int, double, char, int, char)");
//打印對象信息
print();
}

inline Apple::Apple(char test) {
//有參構造函數調用時打印信息
printCalled("Apple", "Apple(int)");
//拋出自定義異常
throw TestException();
}

inline Apple::~Apple() {
//析構函數
printCalled("Apple", "~Apple()");
}

inline void* Apple::operator new(size_t size) {
//operator new
cout << "size_t = " << size << endl;
printCalled("Apple", "void* operator new(size_t)");

//this->print();    錯誤提示:static不能調用non-static
//如果使用this指針在該處調用狂窑,會報錯,operator new會自動轉換為static的函數桑腮,僅用于分配空間泉哈,不會修改對象的內容
//換而言之,此時對象還尚未創(chuàng)建完成破讨,僅僅分配了內存空間丛晦,所以this指針存在也是不可能的

return malloc(size);

}

inline void* Apple::operator new[](size_t size) {
//operator new[]
printCalled("Apple", "void* operator new");
return malloc(size);
}

inline void* Apple::operator new(size_t size, int type) {
//placement new
printCalled("Apple placement new <int>", "void* operator new(size_t, int)");
return malloc(size);
}

inline void* Apple::operator new(size_t size, char type) {
//placement new
cout << "size_t = " << size << endl;
printCalled("Apple placement new <char>", "void* operator new(size_t, char)");

return malloc(size);

}

inline void Apple::operator delete(void* p) {
//operator delete
printCalled("Apple", "void operator delete(void*)");
free(p);
}

inline void Apple::operator delete(void* p, int type) {
//placement delete
//為和此處不可以使用this指針來釋放內存空間呢?
//this->print();嘗試
//如果使用this指針在該出調用提陶,會報錯采呐,operator delete會自動轉換為static的函數,所以形參列表中必須傳入一個指針
printCalled("Apple placement delete <int>", "void operator delete(void*, int)");
free(p);
}

inline void Apple::operator delete[](void* p) {
//array delete
printCalled("Apple", "void operator delete");
free(p);
}

inline void Apple::print() {
//打印Apple的對象信息
Fruit::print();
cout << dec << "\nApple (" << size << ", " << type << ")\n"
<< "size = " << sizeof(*this) << ", " << "address = 0x" << hex << this << endl;
}

inline string operator*(string content,const int count) {
string c = content;
for (int i = 0; i < count; i++) {
content += c;
}
return content;
}

inline void printCalled(const string scope, const string functionName) {
cout << "\n(" << scope << ") "<< functionName << " has been called\n";
}

endif // !FRUIT__EL_

const string s = "\n\n" + x * 50 + "\n\n";
int main()
{
srand((unsigned) time(NULL));
//在棧里面創(chuàng)建對象的測試
cout << s << "\n" "在棧里面創(chuàng)建對象的測試" << endl;
Apple();//臨時對象搁骑,生命周期僅在這一句斧吐,執(zhí)行完就會彈出棧

cout << x * 50 << "\n\n\n";

//在堆里面創(chuàng)建對象的測試
cout << s << "\n" "通過普通的new,在堆里面創(chuàng)建對象的測試" << endl;
Apple* pa = new Apple();
delete pa;  

Apple* pa1 = NULL, *pa2 = NULL;

//測試調用拋出異常的構造函數仲器,重寫了operator new煤率,檢測拋出異常后,會調用哪個delete 
cout << s << "\n" "測試調用拋出異常的構造函數乏冀,重寫了operator new蝶糯,檢測拋出異常后,會調用哪個delete " << endl;
try {
    pa1 = new Apple('1');
}
catch (TestException &e) {
    cout << "\nexception catched\n" << e;
    if(pa1)
        delete pa1;
}

//測試拋出異常的構造函數辆沦,重寫并調用了placement new,檢測拋出異常后昼捍,會調用哪個delete
cout << s << "\n" "測試拋出異常的構造函數,重寫并調用了placement new,檢測拋出異常后肢扯,會調用哪個delete" << endl;
try {
    pa2 = new (1)Apple('1');
}
catch (TestException &e) {
    cout << "\nexception catched\n" << e;
    if (pa2)
        delete pa2;
}

//測試調用一個不成對的placement new妒茬,會調用那個delete
cout << s << "\n" "測試調用一個不成對的placement new,檢測拋出異常后,會調用哪個delete" << endl;
try {
    pa2 = new ('1')Apple('1');  
}
catch (TestException &e) {
    cout << "\nexception catched\n" << e;
    if (pa2)
        delete pa2;
}

//測試調用正常的構造函數的過程蔚晨,使用的是重寫的operator new乍钻,檢測會調用那個delete
cout << s << "\n""測試調用正常的構造函數的過程肛循,使用的是重寫的operator new,檢測會調用那個delete" << endl;
Apple* pa3 = new(1)Apple();
delete pa3;

//數組測試
cout << s << "\n" "數組測試" << endl;
Apple* as1 = new Apple[2];
delete[] as1;



return 0;

}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末银择,一起剝皮案震驚了整個濱河市多糠,隨后出現的幾起案子,更是在濱河造成了極大的恐慌浩考,老刑警劉巖夹孔,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異析孽,居然都是意外死亡搭伤,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門绿淋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闷畸,“玉大人,你說我怎么就攤上這事吞滞∮悠校” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵裁赠,是天一觀的道長殿漠。 經常有香客問我,道長佩捞,這世上最難降的妖魔是什么绞幌? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮一忱,結果婚禮上莲蜘,老公的妹妹穿的比我還像新娘。我一直安慰自己帘营,他們只是感情好票渠,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著芬迄,像睡著了一般问顷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上禀梳,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天杜窄,我揣著相機與錄音,去河邊找鬼算途。 笑死塞耕,一個胖子當著我的面吹牛,可吹牛的內容都是我干的郊艘。 我是一名探鬼主播荷科,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼唯咬,長吁一口氣:“原來是場噩夢啊……” “哼纱注!你這毒婦竟也來了畏浆?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤狞贱,失蹤者是張志新(化名)和其女友劉穎刻获,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體瞎嬉,經...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡蝎毡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了氧枣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沐兵。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖便监,靈堂內的尸體忽然破棺而出扎谎,到底是詐尸還是另有隱情,我是刑警寧澤烧董,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布毁靶,位于F島的核電站,受9級特大地震影響逊移,放射性物質發(fā)生泄漏预吆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一胳泉、第九天 我趴在偏房一處隱蔽的房頂上張望拐叉。 院中可真熱鬧,春花似錦扇商、人聲如沸凤瘦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽廷粒。三九已至,卻和暖如春红且,著一層夾襖步出監(jiān)牢的瞬間坝茎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工暇番, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嗤放,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓壁酬,卻偏偏與公主長得像次酌,于是被迫代替她去往敵國和親恨课。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內容