如何定義一個(gè)只能在(堆/棧)上生成對(duì)象的類
掃描二維碼
隨時(shí)隨地手機(jī)看文章
前幾天C語(yǔ)言與C++面試知識(shí)總結(jié)這篇文章中,有總結(jié)定義一個(gè)只能在堆上(棧上)生成對(duì)象的類以及方法和原因,在里說一下具體實(shí)現(xiàn)
在C++中,類的對(duì)象建立分為兩種:
-
一種是靜態(tài)建立,如A a;
-
靜態(tài)建立一個(gè)類對(duì)象,是由編譯器為對(duì)象在棧空間中分配內(nèi)存,是通過直接移動(dòng)棧頂指針,挪出適當(dāng)?shù)目臻g,然后在這片內(nèi)存空間上調(diào)用構(gòu)造函數(shù)形成一個(gè)棧對(duì)象。使用這種方法,直接調(diào)用類的構(gòu)造函數(shù)。
-
一種是動(dòng)態(tài)建立,如A* ptr=new A;這兩種方式是有區(qū)別的。
-
動(dòng)態(tài)建立類對(duì)象,是使用new運(yùn)算符將對(duì)象建立在堆空間中。這個(gè)過程分為兩步,第一步是執(zhí)行operator new()函數(shù),在堆空間中搜索合適的內(nèi)存并進(jìn)行分配;第二步是調(diào)用構(gòu)造函數(shù)構(gòu)造對(duì)象,初始化這片內(nèi)存空間。這種方法,間接調(diào)用類的構(gòu)造函數(shù)。
1 只能在堆上
方法:將析構(gòu)函數(shù)設(shè)置為私有
原因:C++ 是靜態(tài)綁定語(yǔ)言,編譯器管理?xiàng)I蠈?duì)象的生命周期,編譯器在為類對(duì)象分配??臻g時(shí),會(huì)先檢查類的析構(gòu)函數(shù)的訪問性。若析構(gòu)函數(shù)不可訪問,則不能在棧上創(chuàng)建對(duì)象。
思路:容易想到將構(gòu)造函數(shù)設(shè)為私有。在構(gòu)造函數(shù)私有之后,無(wú)法在類外部調(diào)用構(gòu)造函數(shù)來(lái)構(gòu)造類對(duì)象,只能使用new運(yùn)算符來(lái)建立對(duì)象。然而,前面已經(jīng)說過,new運(yùn)算符的執(zhí)行過程分為兩步,C++提供new運(yùn)算符的重載,其實(shí)是只允許重載operator new()函數(shù),而operator()函數(shù)用于分配內(nèi)存,無(wú)法提供構(gòu)造功能。因此,這種方法不可以。
當(dāng)對(duì)象建立在棧上面時(shí),是由編譯器分配內(nèi)存空間的,調(diào)用構(gòu)造函數(shù)來(lái)構(gòu)造棧對(duì)象。當(dāng)對(duì)象使用完后,編譯器會(huì)調(diào)用析構(gòu)函數(shù)來(lái)釋放棧對(duì)象所占的空間。編譯器管理了對(duì)象的整個(gè)生命周期。如果編譯器無(wú)法調(diào)用類的析構(gòu)函數(shù),情況會(huì)是怎樣的呢?比如,類的析構(gòu)函數(shù)是私有的,編譯器無(wú)法調(diào)用析構(gòu)函數(shù)來(lái)釋放內(nèi)存。所以,編譯器在為類對(duì)象分配??臻g時(shí),會(huì)先檢查類的析構(gòu)函數(shù)的訪問性,其實(shí)不光是析構(gòu)函數(shù),只要是非靜態(tài)的函數(shù),編譯器都會(huì)進(jìn)行檢查。如果類的析構(gòu)函數(shù)是私有的,則編譯器不會(huì)在??臻g上為類對(duì)象分配內(nèi)存。
將析構(gòu)函數(shù)設(shè)為私有,類對(duì)象就無(wú)法建立在棧上。
代碼:
#include<iostream>
using namespace std;
//只在棧上開辟 2020.05.22
class A
{
public:
A(){}
void destory()
{
delete this;
}
private:
~A(){}
};
int main()
{
A a;
return 0;
}
提示析構(gòu)函數(shù)無(wú)法訪問。這樣就只能使用new操作符來(lái)建立對(duì)象,構(gòu)造函數(shù)是公有的,可以直接調(diào)用。類中必須提供一個(gè)destory函數(shù),來(lái)進(jìn)行內(nèi)存空間的釋放。類對(duì)象使用完成后,必須調(diào)用destory函數(shù)。
上述方法的一個(gè)缺點(diǎn)就是,無(wú)法解決繼承問題。如果A作為其它類的基類,則析構(gòu)函數(shù)通常要設(shè)為virtual,然后在子類重寫,以實(shí)現(xiàn)多態(tài)。因此析構(gòu)函數(shù)不能設(shè)為private。還好C++提供了第三種訪問控制,protected。將析構(gòu)函數(shù)設(shè)為protected可以有效解決這個(gè)問題,類外無(wú)法訪問protected成員,子類則可以訪問。
另一個(gè)問題是,類的使用很不方便,使用new建立對(duì)象,卻使用destory函數(shù)釋放對(duì)象,而不是使用delete。(使用delete會(huì)報(bào)錯(cuò),因?yàn)閐elete對(duì)象的指針,會(huì)調(diào)用對(duì)象的析構(gòu)函數(shù),而析構(gòu)函數(shù)類外不可訪問)這種使用方式比較怪異。為了統(tǒng)一,可以將構(gòu)造函數(shù)設(shè)為protected,然后提供一個(gè)public的static函數(shù)來(lái)完成構(gòu)造,這樣不使用new,而是使用一個(gè)函數(shù)來(lái)構(gòu)造,使用一個(gè)函數(shù)來(lái)析構(gòu)。代碼如下,類似于單例模式:
#include<iostream>
using namespace std;
//只在棧上開辟 2020.05.22
class A
{
protected:
A(){}
~A(){}
public:
static A* create()
{
return new A();
}
void destory()
{
delete this;
}
};
int main()
{
A* ptr = A::create();
ptr->destory();
return 0;
}
這樣,調(diào)用create()函數(shù)在堆上創(chuàng)建類A對(duì)象,調(diào)用destory()函數(shù)釋放內(nèi)存。
2 只能在棧上
方法:將 new 和 delete 重載為私有
原因:在堆上生成對(duì)象,使用 new 關(guān)鍵詞操作,其過程分為兩階段:第一階段,使用 new 在堆上尋找可用內(nèi)存,分配給對(duì)象;第二階段,調(diào)用構(gòu)造函數(shù)生成對(duì)象。將 new 操作設(shè)置為私有,那么第一階段就無(wú)法完成,就不能夠在堆上生成對(duì)象。
思路:只有使用new運(yùn)算符,對(duì)象才會(huì)建立在堆上,因此,只要禁用new運(yùn)算符就可以實(shí)現(xiàn)類對(duì)象只能建立在棧上。將operator new()設(shè)為私有即可。
代碼:
class A
{
private :
void * operator new ( size_t t){} // 注意函數(shù)的第一個(gè)參數(shù)和返回值都是固定的
void operator delete ( void * ptr){} // 重載了new就需要重載delete
public :
A(){}
~A(){}
};
參考資料
https://www.nowcoder.com/questionTerminal/0a584aa13f804f3ea72b442a065a7618
最新原創(chuàng)推薦
十大經(jīng)典排序算法(動(dòng)態(tài)演示+代碼)
一文讀懂C語(yǔ)言與C++動(dòng)態(tài)內(nèi)存
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!