我們先了解derived class在調用constructor的時候,首先先調用的是其父類的構造函數
why?
首先我們的derived class obj中有所有的父類class的成員,在derived clas obj中其父類成員的可訪問程度根據父類成員所在位置所定(子類對象可以訪問父類的public和protected成員),所以在我們的子類構造的時候要先構造父類class(主要將其父類成員初始化)
沒有構造函數編譯器會默認分配一個編譯器給你,其功能僅僅是初始化成員而已
如下圖、
#includeclass Base{public:
Base(){std::cout<< "constructor of Base "<<'\n'; }
};
class Derived: public Base{public:
Derived(){std::cout<< "constructor of derived "<< '\n'; }
};
int main(){Derived* d1 = new Derived();
return 0;
}
打印如下
constructor of Base
constructor of derived
當然我們構造子類的時候也可以選擇性的調用父類構造函數
#includeclass Base{public:
Base(){std::cout<< "constructor of Base "<<'\n'; }
Base(int a) : Base_value(a) {std::cout<< "constructor of base and init base_value and base_value is "<< Base_value<< '\n';}
int Base_value;
};
class Derived: public Base{public:
Derived(): Base(){std::cout<< "constructor of derived "<< '\n'; }
Derived(int a): Base(a){std::cout<< "call second constructor of Base_Value"<< '\n';}
};
int main(){Derived* d1 = new Derived(100);
// Derived obj;
return 0;
}
Class繼承中的析構函數我們上面講到了子類中其實是有父類的成員,所以在構造子類的時候先調用父類的構造函數構造父類,有構造那么就有析構函數,析構的順序正好相反,先析構子類,再析構父類如下
#includeclass Base{public:
Base(){std::cout<< "constructor of Base "<<'\n'; }
Base(int a) : Base_value(a) {std::cout<< "constructor of base and init base_value and base_value is "<< Base_value<< '\n';}
~Base(){std::cout<< "destructor of Base"<<'\n';}
int Base_value;
};
class Derived: public Base{public:
Derived(): Base(){std::cout<< "constructor of derived "<< '\n'; }
Derived(int a): Base(a){std::cout<< "call second constructor of Base_Value"<< '\n';}
~Derived(){std::cout<< "destructor of Derived"<< '\n';}
};
int main(){Derived* d1 = new Derived(100);
// Derived obj;
std::cout<< "---------------"<< '\n';
delete d1;
return 0;
}
打印如下
constructor of base and init base_value and base_value is 100
call second constructor of Base_Value
---------------
destructor of Derived
destructor of Base
我們再看下面代碼在delete
derived class的時候只有base的destructor被執(zhí)行,而derived class的destructor并沒有被執(zhí)行
#includeclass Base{public:
virtual void fun() {std::cout<< "base fun"<< std::endl; }
~Base() {std::cout<< "base destructor"<< std::endl;}
};
class Derive: public Base{public:
virtual void fun() {std::cout<< "derived fun"<< '\n';}
~Derive() {std::cout<< "Derive destructor"<<'\n'; }
};
int main(){Base *b1 = new Base();
Base *b2 = new Derive();
b1->fun();
b2->fun();
delete b1;
delete b2;
}
打印如下
base fun
derived fun
base destructor
base destructor
為什么會這樣?我們注意我們new完Derived class后將其賦值給Base class指針,此時我們new完Derived class生成的obj1雖然有derived class自有的成員變量,但是obj1歸根結底是Base class的obj,所以不可訪問Derived Class的額外成員這叫做object slicing
此時又有一個問題誕生,因為我們將子類的對象指針obj1賦值給父類,那么意味著obj1只能使用父類部分的成員,我們直接實例化父類這樣實例化后的對象又比obj1占用的空間小,又簡單不是挺好嗎?為什么還要將子類對象obj1賦值給父類?這里有一個用處,就是父類中的一些成員是virtual,那么子類對象中父類vptr指向的函數為子類override后的地址,如下圖
class X {int x;
string str;
public:
X() {}
virtual ~X() {}
virtual void printAll() {}
};
class Y : public X {int y;
public:
Y() {}
~Y() {}
void printAll() {}
};
Y memory layout
| |
|------------------------------|<------ Y class object memory layout
| int X::x |
stack |------------------------------|
| | int string::len |
| |string X::str ----------------|
| | char* string::str |
\|/ |------------------------------| |-------|--------------------------|
| X::_vptr |------| | type_info Y |
|------------------------------| |--------------------------|
| int Y::y | | address of Y::~Y() |
|------------------------------| |--------------------------|
| o | | address of Y::printAll() |
| o | |--------------------------|
| o |
------|------------------------------|--------
| X::X() |
|------------------------------| |
| X::~X() | |
|------------------------------| |
| X::printAll() | \|/
|------------------------------| text segment
| Y::Y() |
|------------------------------|
| Y::~Y() |
|------------------------------|
| Y::printAll() |
|------------------------------|
| string::string() |
|------------------------------|
| string::~string() |
|------------------------------|
| string::length() |
|------------------------------|
| o |
| o |
| o |
| |
上圖中Y繼承了X,那么Y對象中應該含有X的vptr,上圖中Y的確含有X的vptr,但是X的vptr不再指向X的函數而是指向class Y override后的函數
所以我們在析構函數中加上virtual
關鍵字后子類對象memory layout中對應的父類vptr指針(子類沒有定義virtual函數,所以子類memory layout中沒有關于子類的vptr指針,如果子類對象obj1賦值給父類,那么obj1就會使用其內存中x的vptr指向的虛函數)指向的析構函數變成了子類override的那一個(父類對象memory layout中vptr還是父類virtual function的地址),相反如果我們的子類中沒有對父類的虛函數重載定義,那么子類中的vptr指向的是父類函數的地址,然后編譯器執(zhí)行的時候會先在vptr中找對應的函數地址,因為我們子類沒有重新定義父類虛函數,那么vptr中指向的還是父類對應的虛函數,則最后編譯器執(zhí)行的是父類的函數而不是子類的函數.
你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧
當前文章:C++virtualdestructor-創(chuàng)新互聯
文章源于:http://www.rwnh.cn/article30/dohepo.html
成都網站建設公司_創(chuàng)新互聯,為您提供域名注冊、商城網站、企業(yè)網站制作、服務器托管、小程序開發(fā)、App設計
聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯