c++如何控制對象的創(chuàng)建方式(禁止創(chuàng)建棧對象or堆對象)和創(chuàng)建的數(shù)量
我們知道,C++將內(nèi)存劃分為三個邏輯區(qū)域:堆、棧和靜態(tài)存儲區(qū)。既然如此,我稱位于它們之中的對象分別為堆對象,棧對象以及靜態(tài)對象。通常情況下,對象創(chuàng)建在堆上還是在棧上,創(chuàng)建多少個,這都是沒有限制的。但是有時會遇到一些特殊需求。
1.禁止創(chuàng)建棧對象
禁止創(chuàng)建棧對象,意味著只能在堆上創(chuàng)建對象。創(chuàng)建棧對象時會移動棧頂指針以“挪出”適當大小的空間,然后在這個空間上直接調(diào)用類的構(gòu)造函數(shù)以形成一個棧對象。而當棧對象生命周期結(jié)束,如棧對象所在函數(shù)返回時,會調(diào)用其析構(gòu)函數(shù)釋放這個對象,然后再調(diào)整棧頂指針收回那塊棧內(nèi)存。在這個過程中是不需要operator new/delete操作的,所以將operator new/delete設置為private不能達到目的。
可以將構(gòu)造函數(shù)或析構(gòu)函數(shù)設為私有的,這樣系統(tǒng)就不能調(diào)用構(gòu)造/析構(gòu)函數(shù)了,當然就不能在棧中生成對象了。這樣的確可以,但有一點需要注意,那就是如果我們將構(gòu)造函數(shù)設置為私有,那么我們也就不能用new來直接產(chǎn)生堆對象了,因為new在為對象分配空間后也會調(diào)用它的構(gòu)造函數(shù)。所以,如果將構(gòu)造函數(shù)和析構(gòu)函數(shù)都聲明為private會帶來較大的副作用,最好的方法是將析構(gòu)函數(shù)聲明為private,而構(gòu)造函數(shù)保持為public。
再進一步,將析構(gòu)函數(shù)設為private除了會限制棧對象生成外,還有其它影響嗎?是的,這還會限制繼承。如果一個類不打算作為基類,通常采用的方案就是將其析構(gòu)函數(shù)聲明為private。為了限制棧對象,卻不限制繼承,我們可以將析構(gòu)函數(shù)聲明為protected,這樣就兩全其美了。如下代碼所示:
class NoStackObject{
protected:
~NoStackObject(){}
public:
void destroy(){
delete this ;//調(diào)用保護析構(gòu)函數(shù)
}
};
上面的類在創(chuàng)建棧對象時,如NoStackObject obj;時編譯將會報錯,而采用new的方式,編譯就會通過。需要注意一點的是,通過new創(chuàng)建堆對象時,在手動釋放對象內(nèi)存時,我們需要調(diào)用其析構(gòu)函數(shù),這時就需要一點技巧來輔助——引入偽析構(gòu)函數(shù)destory,如上面的代碼所示。
方法拓展。
仔細一看,我們會發(fā)現(xiàn)上面的方法讓人別扭。我們用new創(chuàng)建一個對象,卻不是用delete去刪除它,而是要用destroy方法。很顯然,用戶會不習慣這種怪異的使用方式。所以,可以將構(gòu)造函數(shù)也設為private或protected。這又回到了上面曾試圖避免的問題,即不用new,那么該用什么方式來生成一個對象了?我們可以用間接的辦法完成,即讓這個類提供一個static成員函數(shù)專門用于產(chǎn)生該類型的堆對象。(設計模式中的singleton模式就可以用這種方式實現(xiàn)。)讓我們來看看:
class NoStackObject {
protected:
NoStackObject() { }
~NoStackObject() { }
public:
static NoStackObject* creatInstance() {
return new NoStackObject() ;//調(diào)用保護的構(gòu)造函數(shù)
}
void destroy() {
delete this ;//調(diào)用保護的析構(gòu)函數(shù)
}
};
現(xiàn)在可以這樣使用NoStackObject類了:
NoStackObject* hash_ptr = NoStackObject::creatInstance() ; ... ... //對hash_ptr指向的對象進行操作 hash_ptr->destroy() ; hash_ptr = NULL ; //防止使用懸掛指針
現(xiàn)在感覺是不是好多了,生成對象和釋放對象的操作一致了。
2.禁止創(chuàng)建堆對象
我們已經(jīng)知道,產(chǎn)生堆對象的唯一方法是使用new操作,如果我們禁止使用new不就行了么。再進一步,new操作執(zhí)行時會調(diào)用operator new,而operator new是可以重載的。方法有了,就是使new operator 為private,為了對稱,最好將operator delete也重載為private。
class NoStackObject{
private:
static void* operator new(size_t size);
static void operator delete(void* ptr);
};
//用戶代碼
NoStackObject obj0; //OK
static NoStackObject obj1; //OK
NoStackObject * pObj2 = new NoStackObject; //ERROR
如果也想禁止堆對象數(shù)組,可以把operator new[]和operator delete[]也聲明為private。
這里同樣在繼承時存在問題,如果派生類改寫了operator new和operator delete并聲明為public,則基類中原有的private版本將失效,參考如下代碼:
class NoStackObject{
protected:
static void* operator new(size_t size);
static void operator delete(void* ptr);
};
class NoStackObjectSon:public NoStackObject{
public:
static void* operator new(size_t size){ //非嚴格實現(xiàn),僅作示意之用
return malloc(size);
};
static void operator delete(void* ptr){ //非嚴格實現(xiàn),僅作示意之用
free(ptr);
};
};
//用戶代碼
NoStackObjectSon* pObj2 = new NoStackObjectSon; //OK
3.控制實例化對象的個數(shù)
在游戲設計中,我們采用類CGameWorld作為游戲場景的抽象描述。然而在游戲運行過程中,游戲場景只有一個,也就是對CGameWorld對象的只有一個。對于對象的實例化,有一點是十分確定的:要調(diào)用構(gòu)造函數(shù)。所以,如果想控制CGameWorld的實例化對象只有一個,最簡單的方法就是將構(gòu)造函數(shù)聲明為private,同時提供一個static對象。如下:
class CGameWorld
{
public:
bool Init();
void Run();
private:
CGameWorld();
CGameWorld(const CGameWorld& rhs);
friend CGameWorld& GetSingleGameWorld();
};
CGameWorld& GetSingleGameWorld()
{
static CGameWorld s_game_world;
return s_game_world;
}
這個設計有三個要點:
(1)類的構(gòu)造函數(shù)是private,阻止對象的建立;
(2)GetSingleGameWorld函數(shù)被聲明為友元,避免了私有構(gòu)造函數(shù)引起的限制;
(3)s_game_world為一個靜態(tài)對象,對象唯一。
當用到CGameWorld的唯一實例化對象時,可以如下:
GetSingleGameWorld().Init(); GetSingleGameWorld().Run();
如果有人對GetSingleGameWorld是一個全局函數(shù)有些不爽,或者不想使用友元,將其聲明為類CGameWorld的靜態(tài)函數(shù)也可以達到目的,如下:
class CGameWorld
{
public:
bool Init();
void Run();
static CGameWorld& GetSingleGameWorld();
private:
CGameWorld();
CGameWorld(const CGameWorld& rhs);
};
這就是設計模式中著名的單件模式:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
如果我們想讓對象產(chǎn)生的個數(shù)不是一個,而是最大為N(N>0)個。可以在類內(nèi)部設置一個靜態(tài)計數(shù)變量,在調(diào)用構(gòu)造函數(shù)時,該變量加1,當調(diào)用析構(gòu)函數(shù)時,該變量減1。如下:
class CObject
{
public:
CObject();
~CObject();
private:
static size_t m_nObjCount;
...
};
CObject::CObject()
{
if (m_nObjCount > N)
throw;
m_nObjCount++;
}
CObject::~CObject()
{
m_nObjCount--;
}
size_t CObject::m_nObjCount;
掌握控制類的實例化對象個數(shù)的方法。當實例化對象唯一時,采用設計模式中的單件模式;當實例化對象為N(N>0)個時,設置計數(shù)變量是一個思路。
閱讀上面的示例代碼還需要注意拋出異常時沒有對象,即throw后沒有對象,有兩種含義:
(1)如果throw;在catch塊中或被catch塊調(diào)用的函數(shù)中出現(xiàn),表示重新拋出異常。throw;表達式將重新拋出當前正在處理的異常。 我們建議采用該形式,因為這將保留原始異常的多態(tài)類型信息。重新引發(fā)的異常對象是原始異常對象,而不是副本。
(2)如果throw;出現(xiàn)在非catch塊中,表示拋出不能被捕獲的異常,即使catch(…)也不能將其補捕獲。
4.小結(jié)
堆對象,棧對象以及靜態(tài)對象統(tǒng)稱為內(nèi)存對象,如果要把內(nèi)存對象理解的更為深入,推薦看看《深入探索C++對象模型》這本書。
以上就是c++如何控制對象的創(chuàng)建方式(禁止創(chuàng)建棧對象or堆對象)和創(chuàng)建的數(shù)量的詳細內(nèi)容,更多關(guān)于c++控制對象的創(chuàng)建方式與數(shù)量的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++隱式類型轉(zhuǎn)換運算符operator type()用法詳解
這篇文章主要介紹了C++隱式類型轉(zhuǎn)換運算符operator type()用法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-06-06
C++實現(xiàn)LeetCode(82.移除有序鏈表中的重復項之二)
這篇文章主要介紹了C++實現(xiàn)LeetCode(82.移除有序鏈表中的重復項之二),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07
visual?studio?將編譯后的dll等文件自動復制到指定目錄的方法
這篇文章主要介紹了visual?studio?將編譯后的dll等文件自動復制到指定目錄,本文給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2024-03-03

