1.設計一個類不能被繼承 。
解法一、將該類的構造函數設爲私有函數
在C++中子類的構造函數會自動調用父類的構造函數,子類的析構函數也會自動調用父類的析構函數,要想一個類不能被繼承,我們只要把它的構造函數和析構函數都定義爲私有函數,那麼當一個類試圖從它那繼承的時候,必然會由於調用構造函數、析構函數而導致編譯錯誤。那麼既然這個類的構造和析構函數都是私有的,我們就必須通過定義公有的靜態函數來創建和釋放類的實例。
class FinalClass1
{
public :
static FinalClass1* GetInstance()
{
return new FinalClass1;
}
static void DeleteInstance( FinalClass1* pInstance)
{
delete pInstance;
pInstance = 0;
}
private :
FinalClass1() {}
~FinalClass1() {}
};
這個類不能被繼承,而且只能得到堆上的實例,得不到棧上的實例。
解法二、虛擬繼承
template <typename T> class MakeClass
{
friend T;
private:
MakeClass(){}
~MakeClass(){}
};
class FinalClass2
{
public:
FinalClass2(){}
~FinalClass2(){}
};
2.設計一個類只能在堆上創建對象。
分配堆區域內的對象屬於動態建立類對象過程。
編譯器對他們的內存分配是在運行時動態分配的,使用new運算符將對象建立在堆空間中。這個過程分爲兩步:
第一步,執行operator new()函數,在對中搜索合適的內存並進行分配;
第二步,調用構造函數構造對象,初始化這片內存空間。使用這種方法,間接調用類的構造函數。
那麼怎樣設計一個類能夠只在堆上創建對象呢?
很容易就想到的一個方法是把類的構造函數聲明爲私有,就無法在類的外部調用類的構造函數建立對象,只能使用new運算符來建立對象。前面已經說過,new運算符的執行過程分爲兩步,C++提供new運算符的重載其實只是允許重載operator new()函數,不能重載new運算符,而operator new()函數只用於分配內存,無法提供構造函數,所以,我們再定義一個GetObj函數,用於在堆上new對象,通過GetObj函數,建立的對象都是在堆上的new出來的,將函數聲明爲靜態的,就可以通過域作用訪問符訪問GetObj函數,在堆上建立對象。(在C++中靜態成員函數也是類函數,及這個函數不屬於某個具體的對象,而是屬於一個類的,這個類實例化的每個成員都可用,同時,這個類也可以直接調用這個函數而不用實例化一個對象。)
class BB
{
public:
static BB& GetObj(int b1 = 0,int b2 = 0)
{
return *(new BB(b1,b2));
}
private:
BB(int b1 = 0,int b2 = 0)
:_b1(b1)
,_b2(b2)
{}
int _b1;
int _b2;
};
void Test()
{
BB b = BB::GetObj();
}
3.設計一個類只能在棧上創建對象。
分配在棧區域內的對象屬於靜態建立類對象過程。
編譯器對它們的內存分配是在編譯階段就完成的,是通過直接移動棧頂指針,挪出適當的空間,然後在這片內存空間上調用構造函數形成一個棧對象。使用這種方法,直接調用類的構造函數。
那麼怎樣設計一個類能夠只在棧上創建對象呢?
只有使用new運算符,對象纔會建立在堆上,所以只要禁用new運算符就可以實現類對象只能建立在棧上,將operator new()設爲私有就可以了,一定要記得,重載了new就要重載delete
class CC
{
public:
CC()
{}
~CC()
{}
private:
void* operator new(size_t)//重載operator new() ,這裏函數返回值和參數都是固定的
{}
void operator delete(void* ptr)//也要重載operator delete()
{}
};