c++ 類的定製

這個話題比較大了,所以我們往簡單點說,那就是我們想要控制一個類的行為,嗯,這裡說的不是一個類對象的行為,好吧,如果接觸C++不是太深的話這可能不太好理解,因為似乎我們都不怎麼關心這個問題,我們往往關心的是一個對象的行為,但是不表示我們就不應該關心類的行為。

那麼類有哪些行為是我們可以或者說應該關心的?

首先我們來說類的構建方式,類的構建方式不都很簡單嗎?直接聲明定義或者new出來即可,那麼問題來了,直接聲明定義出來的對象都是棧對象,但是計算機棧空間遠不及堆空間大,所以可能我們會要求使用該類的人必須將對象分配在堆上面,那麼問題來了,你不知道誰在使用你所提供的類,所以你不可能對個使用你class的人都耳提面命的告訴他們你必須要new出來,否則可能會導致某種不可預期的問題,所以為了讓使用該類的人必須在堆上構建,那麼就要從根源上來解決該問題,讓他必須new出來,而打算在棧上構建該對象則無法通過編譯,要定製這樣的話很簡單,我們只需要把析構函數設定為protected或者private即可。

//+---------------------------

class Test{

public:

Test(){}

void Destroy(){

delete this;

}

protected:

~Test(){}

};

int main(){

//

// 正確的使用方式

//

Test* obj = new Test();

obj->Destroy();

//

// 下面的方式無法通過編譯

//

Test obj;

system("pause")

return 0;

}

//+-----------------------------

上面是讓類只能在堆上構建的技巧,那麼如果讓類只能在棧上構建呢?也就是我們不可以使用new來完成該行為,比如Direct3D裡面的XNA上的XMMATRIX就只能在棧上才能保證16字節對齊(好吧,幾字節對齊記得不太清,總之如果選擇堆內存會出現頻繁崩潰),那麼如何實現這樣的類呢?實現方法也是相當的簡單,我們只需要將new操作符都實現為私有的即可,既然是私有的那麼就可以不需要實現,而只需要聲明即可:

//+-----------------------------

class Test{

public:

Test(){}

~Test(){}

protected:

static void* operator new(size_t size);

static void operator delete(void* ptr, std::size_t size);

};

int main(){

//

// 錯誤的使用方式,無法通過編譯

//

Test* obj = new Test();

//

// 正確的使用方式

//

Test obj;

system("pause")

return 0;

}

//+---------------------------------

這是將類設計為只能在棧上分配的技巧,當然你可以這麼認為:使用malloc獲取內存,然後轉換為Test*:

//+-------------------------------------

Test* obj = (Test*)malloc(sizeof(Test));

//+-------------------------------------

但只是一塊raw內存而已,並沒有實質上的意義,雖然對於上面我們這個空類來說是可行的,但是當我們在構造函數中有初始化行為時這就不可行,比如:

//+--------------------------------------

class Test{

public:

Test(){

m_value = 10;

}

~Test(){}

int value() const{

return m_value;

}

protected:

static void* operator new(size_t size);

static void operator delete(void* ptr, std::size_t size);

private:

int m_value;

};

int main(){

Test* obj = (Test*)malloc(sizeof(Test));

std::cout << obj->value() << std::endl;

free(obj);

system("pause");

return 0;

}

//+-----------------------------------------

我們會看到打印的結果是一個隨機數,所以這不是正確的行為。

接下來我們設計整個進程中只存在一個類對象,其實這屬於一種設計模式——單例模式,單例簡單點說就是一個經過改進後的全局對象,然而我們不能簡單的以為是靜態數據 + 靜態函數 = 單例。

//+------------------------------------

class Test{……};

class MyOneTest{

public:

static void doSomething(Test& test);

private:

static std::queue testQueue;

};

Test test;

MyOneTest::doSomething(test);

//+-----------------------------------

看上去沒啥問題,運用起來也沒啥問題,但在某些使用情況下有很多問題,比如我的行為要求有所改變,doSomething不再適合(當然如果這不是個靜態函數而是個虛函數那麼這一切都不是問題),這一切的修改需要建立在擁有源碼的基礎上才行,同時靜態數據加靜態函數的做法對數據的初始化和清理同樣帶來難題。

關於單例的實現方式有多種,這裡我就主要說說我使用的方案:

//+------------------------------------

class Singleton{

public:

static Singleton* Instance(){

if(__sPtr){

return __sPtr;

}

else{

__sPtr = new Singleton;

return __sPtr;

}

}

static void Destroy(){

if(__sPtr){

delete __sPtr;

__sPtr = nullptr;

}

}

protected:

Singleton(){};

~Singleton(){}

Singleton(const Singleton& other) = delete;

Singleton& operator(const Singleton& other) = delete;

private:

static Singleton* __sPtr;

};

Singleton* Singleton::__sPtr = nullptr;

//+---------------------------------

這種設計的好處是我不需要的時候就不會有Singleton對象的創建,這對於開銷很大的對象來說很重要,同時還有多態的性質,因為我們是動態創建,考慮一下我們為什麼不用下面的方式:

//+----------------------------------

class Singleton{

public:

每天會更新論文和視頻,還有如果想學習c++知識在晚上8.30免費觀看這個直播:https://ke.qq.com/course/131973#tuin=b52b9a80


分享到:


相關文章: