題目內容:
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;
}