(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.子類對象創建時:會先創建父類對象,再創建子類對象(之前講的裝快遞的故事)
#### 2.子-類對象銷毀時:會先銷毀子類對象,再銷毀父類對象(之前講的拆快遞的故事)
#### 3.子類的大小 = 父類成員屬性的大小 + 虛函數指針(不存在為0Byte) + 子類成員屬性的大小
#### 4.子類的內存中包括父類對象(由內存地址相同可知)
#### 5.placement new和placement delete最好能成對出現,除非保證創建對象時不會產生異常
#### 6.placement delete只會在new對象時拋出異常時才會調用,并且是成對調用(形參列表相同)。
#### 7.placement new創建對象如果不拋出異常,不會調用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構造函數(節選)

.....
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構造函數(節選)

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));
//在棧里面創建對象的測試
cout << s << "\n" "在棧里面創建對象的測試" << endl;
Apple();//臨時對象,生命周期僅在這一句,執行完就會彈出棧
.....
}
.....


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

- 結論
1.子類對象創建時:會先創建父類對象,再創建子類對象(之前講的裝快遞的故事)
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的函數,僅用于分配空間,不會修改對象的內容
//換而言之,此時對象還尚未創建完成,僅僅分配了內存空間,所以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的函數,僅用于分配空間,不會修改對象的內容
//換而言之,此時對象還尚未創建完成,僅僅分配了內存空間,所以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));
.......

//在堆里面創建對象的測試
cout << s << "\n" "通過普通的new,在堆里面創建對象的測試" << 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創建對象產生異常時的處理
- 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的函數,僅用于分配空間,不會修改對象的內容
//換而言之,此時對象還尚未創建完成,僅僅分配了內存空間,所以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的函數,僅用于分配空間,不會修改對象的內容
//換而言之,此時對象還尚未創建完成,僅僅分配了內存空間,所以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創建對象時,如果創建對象時產生異常,處理異常時`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的函數,僅用于分配空間,不會修改對象的內容
//換而言之,此時對象還尚未創建完成,僅僅分配了內存空間,所以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 創建對象時,不產生異常的情況
- 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的函數,僅用于分配空間,不會修改對象的內容
//換而言之,此時對象還尚未創建完成,僅僅分配了內存空間,所以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的函數,僅用于分配空間,不會修改對象的內容
//換而言之,此時對象還尚未創建完成,僅僅分配了內存空間,所以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創建對象不產生異常的結果](http://upload-images.jianshu.io/upload_images/5688965-b9f43aff7b4c1435.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 結論
1.調用placement new來創建對象時,如果沒有產生異常,在delete時會調用operator delete
---
### 測試六:placement new 創建對象時,沒有成對的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的函數,僅用于分配空間,不會修改對象的內容
//換而言之,此時對象還尚未創建完成,僅僅分配了內存空間,所以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,那么必須保證,** 創建對象時不會產生異常 **,否則就會造成內存泄漏。***如果存在產生異常的可能性,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的函數,僅用于分配空間,不會修改對象的內容
//換而言之,此時對象還尚未創建完成,僅僅分配了內存空間,所以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創建和銷毀的情況](http://upload-images.jianshu.io/upload_images/5688965-39b53519113bacc2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 結論
1.對于數組創建,使用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的函數,僅用于分配空間,不會修改對象的內容
//換而言之,此時對象還尚未創建完成,僅僅分配了內存空間,所以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的函數,僅用于分配空間,不會修改對象的內容
//換而言之,此時對象還尚未創建完成,僅僅分配了內存空間,所以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));
//在棧里面創建對象的測試
cout << s << "\n" "在棧里面創建對象的測試" << endl;
Apple();//臨時對象,生命周期僅在這一句,執行完就會彈出棧

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

//在堆里面創建對象的測試
cout << s << "\n" "通過普通的new,在堆里面創建對象的測試" << 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閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容