常州網(wǎng)站設(shè)計(jì)湛江公司電話網(wǎng)絡(luò)營(yíng)銷推廣的基本手段
目錄
- 一、設(shè)計(jì)一個(gè)類,不能被拷貝
- 二、設(shè)計(jì)一個(gè)類,只能在堆上創(chuàng)建對(duì)象
- 三、設(shè)計(jì)一個(gè)類,只能從棧上創(chuàng)建對(duì)象
- 四、設(shè)計(jì)一個(gè)類,不能被繼承
- 五、設(shè)計(jì)一個(gè)類,只能創(chuàng)建一個(gè)對(duì)象(單例模式)
- 5.1 餓漢模式
- 5.2 懶漢模式
一、設(shè)計(jì)一個(gè)類,不能被拷貝
//1、請(qǐng)?jiān)O(shè)計(jì)一個(gè)類,不能被拷貝
//
// 拷貝一個(gè)類,要么調(diào)用拷貝構(gòu)造函數(shù),要么調(diào)用賦值重載函數(shù),所以要令一個(gè)類不能
// 被拷貝,只需要讓該類不能調(diào)用拷貝構(gòu)造和賦值重載函數(shù)就可以了。
//
// 在C++98語(yǔ)法下,只需要把該類的拷貝構(gòu)造函數(shù)和賦值重載函數(shù)聲明為私有,
// 并且不定義即可
//
//原因:拷貝構(gòu)造函數(shù)和賦值重載函數(shù)只要我們聲明了,編譯器就不會(huì)再默認(rèn)生成,但是
//我們又不定義它,所以這兩個(gè)函數(shù)就不能被調(diào)用,如果定義了,反而在類內(nèi)部會(huì)被調(diào)用
// 拷貝構(gòu)造函數(shù)和賦值重載函數(shù),不符合題意;并且我們聲明為私有函數(shù),別人想要
//從類外面自己定義也是做不到的,所以這樣的類是不能被拷貝的class A
{
public:
private:A(const A& a);A& operator=(const A& a);
};// C++11語(yǔ)法下:
//
// C++11擴(kuò)展delete的用法,delete除了釋放new申請(qǐng)的資源外,如果在默認(rèn)成員函數(shù)后跟上 = delete,
// 表示讓編譯器刪除掉該默認(rèn)成員函數(shù)。
// 所以直接用關(guān)鍵字delete把拷貝構(gòu)造函數(shù)和賦值重載函數(shù)刪除掉即可
class A
{
private:A(const A& a) = delete;A& operator=(const A& a) = delete;
};
二、設(shè)計(jì)一個(gè)類,只能在堆上創(chuàng)建對(duì)象
//2、請(qǐng)?jiān)O(shè)計(jì)一個(gè)類,只能在堆上創(chuàng)建對(duì)象
//
//方法一:要想設(shè)計(jì)一個(gè)類只能在堆上創(chuàng)建對(duì)象,只需要將析構(gòu)函數(shù)私有即可//原因:因?yàn)橹灰x出對(duì)象的,對(duì)象是自定義類型,必然需要調(diào)用構(gòu)造
// 函數(shù)和析構(gòu)函數(shù),而現(xiàn)在把析構(gòu)函數(shù)私有,則定義的對(duì)象在銷毀的時(shí)候
// 無(wú)法調(diào)用析構(gòu)函數(shù),就一定會(huì)報(bào)錯(cuò)
//但是如果是在堆上創(chuàng)建對(duì)象,返回值是一個(gè)對(duì)象的指針,指針是內(nèi)置類型,
//不會(huì)調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù),所以即使析構(gòu)函數(shù)是私有的,也可以通過
//new在堆上創(chuàng)建對(duì)象
class HeapOnly
{
public:void func(){cout << "func" << endl;}void Destroy(){//這里delete掉this指針等于是釋放了調(diào)用//該函數(shù)的類的指針,這種寫法是正確的delete this;cout << "~HeapOnly()" << endl;}private://析構(gòu)函數(shù)私有~HeapOnly(){}private:int _b;
};//方法二:把構(gòu)造函數(shù)私有,然后提供一個(gè)CreateObj的函數(shù),
// 這個(gè)函數(shù)內(nèi)部用new創(chuàng)建一個(gè)對(duì)象,然后返回對(duì)象的指針即可,
// 但是要注意把拷貝構(gòu)造和賦值重載函數(shù)delete掉,防止別人
// 在棧上構(gòu)造對(duì)象
//
class HeapOnly
{
public:static HeapOnly* CreateObj(){HeapOnly* ptr = new HeapOnly;return ptr;}
private:HeapOnly(){}HeapOnly(const HeapOnly& ho) = delete;HeapOnly& operator=(const HeapOnly& ho) = delete;
};int main()
{//static B b;HeapOnly* pb = new HeapOnly;pb->func();//HeapOnly b(*pb);pb->Destroy();return 0;
}//int main()
//{
// HeapOnly* ho = HeapOnly::CreateObj();
//
// return 0;
//}
三、設(shè)計(jì)一個(gè)類,只能從棧上創(chuàng)建對(duì)象
//3、請(qǐng)?jiān)O(shè)計(jì)一個(gè)類,只能在棧上創(chuàng)建對(duì)象
//不能完全設(shè)計(jì)出只在棧上創(chuàng)建對(duì)象的類;
//沿用2的設(shè)計(jì)思路,把構(gòu)造函數(shù)私有,然后提供一個(gè)CreateObj的函數(shù),
//該函數(shù)返回一個(gè)在棧上創(chuàng)建的對(duì)象。
//因?yàn)檫@個(gè)返回的是臨時(shí)對(duì)象,所以不能把拷貝構(gòu)造函數(shù)和賦值函數(shù)delete掉,
//因?yàn)閭髦捣祷貙?duì)象需要被拷貝。
class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();}void func(){cout << "StackOnly()" << endl;}private:StackOnly(){}// 對(duì)一個(gè)類實(shí)現(xiàn)專屬operator new,這句代碼的意思是把用new//創(chuàng)建該類對(duì)象的方法delete掉,外部不能再利用new來創(chuàng)建對(duì)象了void* operator new(size_t size) = delete;};int main()
{StackOnly so = StackOnly::CreateObj();cout << &so << endl;int a = 0;cout << &a << endl;so.func();//因?yàn)橛脩艨赡苡胣ew調(diào)用拷貝構(gòu)造函數(shù)在堆上創(chuàng)建對(duì)象,所以需要// 把類的專屬的operator new給delete掉,禁止用new在堆上創(chuàng)建對(duì)象//StackOnly* pso = new StackOnly(so);//這個(gè)類唯一不能禁止的就是在靜態(tài)區(qū)創(chuàng)建對(duì)象,因?yàn)橐跅I蟿?chuàng)建對(duì)象就//一定要有拷貝構(gòu)造函數(shù),有拷貝構(gòu)造函數(shù)就可以在靜態(tài)區(qū)創(chuàng)建靜態(tài)對(duì)象static StackOnly so1= StackOnly::CreateObj();static int b = 0;cout << &so1 << endl;cout << &b << endl;return 0;
}
四、設(shè)計(jì)一個(gè)類,不能被繼承
//4. 請(qǐng)?jiān)O(shè)計(jì)一個(gè)類,不能被繼承
//
//C++98語(yǔ)法
//構(gòu)造函數(shù)私有,然后提供一個(gè)CreateObj函數(shù)創(chuàng)建對(duì)象
// 原因:構(gòu)造函數(shù)私有,也就意味著構(gòu)造函數(shù)不能被顯式地調(diào)用,
// 因?yàn)樵诶^承體系中,子類成員中父類的成員必須調(diào)用父類的構(gòu)造函數(shù)
// 初始化父類那一部分成員的,所以如果把父類的構(gòu)造函數(shù)私有,子類
// 就沒有辦法調(diào)用父類的構(gòu)造函數(shù)初始化父類那一部分成員,所以就繼承不了
//
class Final
{
public:
private:Final(int f):_f(f){}int _f = 0;
};class A:public Final
{
public://A的構(gòu)造函數(shù)無(wú)法調(diào)用Final的構(gòu)造函數(shù),所以Final類不能被繼承A():Final(2),_a(1){}private:int _a;
};
//C++11語(yǔ)法:
//C++11提供了一個(gè)關(guān)鍵字final,即直接在類后面加上final表示該類不能被繼承
class Final final
{
public:Final(int f):_f(f){}private:int _f = 10;
};這里就會(huì)報(bào)錯(cuò)了,因?yàn)镕inal是不可被繼承的
//class F :public Final
//{
//
//};
五、設(shè)計(jì)一個(gè)類,只能創(chuàng)建一個(gè)對(duì)象(單例模式)
設(shè)計(jì)模式:
設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。
使用設(shè)計(jì)模式的目的:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。 設(shè)計(jì)模式使代碼編寫真正工程化;設(shè)計(jì)模式是軟件工程的基石脈絡(luò),如同大樓的結(jié)構(gòu)一樣。
單例模式:
一個(gè)類只能創(chuàng)建一個(gè)對(duì)象,即單例模式,該模式可以保證系統(tǒng)中該類只有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn),該實(shí)例被所有程序模塊共享。比如在某個(gè)服務(wù)器程序中,該服務(wù)器的配置
信息存放在一個(gè)文件中,這些配置數(shù)據(jù)由一個(gè)單例對(duì)象統(tǒng)一讀取,然后服務(wù)進(jìn)程中的其他對(duì)象再通過這個(gè)單例對(duì)象獲取這些配置信息,這種方式簡(jiǎn)化了在復(fù)雜環(huán)境下的配置管理。
5.1 餓漢模式
//1、餓漢模式:一開始(main函數(shù)之前)就創(chuàng)建單例對(duì)象
//優(yōu)點(diǎn):簡(jiǎn)單
//缺點(diǎn):
// (1) 如果單例對(duì)象要初始化的內(nèi)容很多,啟動(dòng)速度慢。
// (2) 如果A單例對(duì)象的創(chuàng)建依賴B單例對(duì)象,要求B對(duì)象先創(chuàng)建好,但是我們無(wú)法保證先讓B單例對(duì)象創(chuàng)建好。
// (3) 如果單例對(duì)象很大,占用資源很多,但是單例對(duì)象創(chuàng)建出來之后不是立刻使用,
// 會(huì)占用著大量的內(nèi)存,導(dǎo)致其它需要內(nèi)存的地方獲取不到內(nèi)存。
namespace hungry
{class Singalton{public:static Singalton& GetInstance(){return _Inst;}private://構(gòu)造函數(shù)私有,防止別人隨意創(chuàng)建對(duì)象Singalton(){}//把拷貝構(gòu)造函數(shù)和賦值重載函數(shù)刪除掉,防止拷貝Singalton(const Singalton& sg) = delete;Singalton& operator=(const Singalton& sg) = delete;//因?yàn)槿种挥形ㄒ坏囊粋€(gè)對(duì)象,如何保證我們每次獲取到的都是同一個(gè)對(duì)象呢?// 因?yàn)殪o態(tài)的全局變量在main函數(shù)之前就會(huì)定義好的,所以靜態(tài)的全局變量是全局唯一的。// 所以這里一般都是在類里面聲明一個(gè)靜態(tài)的Singalton對(duì)象(類外定義),// 在Singalton類里面可以聲明Singalton的靜態(tài)對(duì)象嗎?// 聲明靜態(tài)對(duì)象static Singalton是可以的,// 但是聲明普通對(duì)象Singalton就不行,為什么呢?// 因?yàn)槁暶髌胀▽?duì)象的話就會(huì)出現(xiàn)無(wú)限套娃的情況了,對(duì)象里面又會(huì)套一個(gè)對(duì)象;但// 是聲明靜態(tài)的Singalton對(duì)象為什么就可以了呢?// 因?yàn)殪o態(tài)的Singalton對(duì)象本身并不存在于Singalton類的空間里面,而是存在于// 靜態(tài)區(qū)中,屬于所有對(duì)象共有的,所以不存在套娃的情況的,所以可以聲明靜態(tài)的Singalton對(duì)象的。// // 同時(shí)要注意,這里只是聲明,普通靜態(tài)對(duì)象必須在類外面定義,但是const static對(duì)象比較特殊,// 可以在類內(nèi)定義這樣每一次調(diào)用GetInstance的時(shí)候就返回這個(gè)靜態(tài)的對(duì)象就可以保證每次返回的都是// 同一個(gè)_Inst了static Singalton _Inst;// 在C++中,允許在類內(nèi)部聲明一個(gè)本類的靜態(tài)對(duì)象的原因是為了方便和靈活性,//當(dāng)聲明一個(gè)類的靜態(tài)成員時(shí),編譯器只需要知道該成員的類型和名稱,而不需要//知道成員的具體定義和大小。這樣做的好處是可以避免一些循環(huán)依賴的問題。如// 果不允許在類內(nèi)部聲明本類的靜態(tài)對(duì)象,那么在類定義之前就無(wú)法實(shí)例化包含靜// 態(tài)成員的對(duì)象,因?yàn)樵陬惗x之前,編譯器還不知道該類的完整定義。//通過在類內(nèi)部聲明本類的靜態(tài)對(duì)象,可以為程序提供更高的靈活性。在類定義之后// 的任何位置,都可以在需要的時(shí)候進(jìn)行定義和初始化這個(gè)靜態(tài)對(duì)象。這樣,程序員// 可以根據(jù)需要更具靈活性地控制對(duì)象的創(chuàng)建和使用。//需要注意的是,在定義之前使用這個(gè)靜態(tài)對(duì)象可能會(huì)導(dǎo)致未定義的行為,因此在類// 定義之后的某個(gè)地方,一定要進(jìn)行靜態(tài)對(duì)象的定義和初始化,以確保它的正確使用。};//定義,在main函數(shù)之前就已經(jīng)創(chuàng)建好了單例對(duì)象Singalton Singalton::_Inst;
}int main()
{//每次調(diào)用GetInstance獲取到的對(duì)象都是同一個(gè)cout << &hungry::Singalton::GetInstance() << endl;cout << &hungry::Singalton::GetInstance() << endl;cout << &hungry::Singalton::GetInstance() << endl;return 0;
}
如果這個(gè)單例對(duì)象在多線程高并發(fā)環(huán)境下頻繁使用,性能要求較高,那么顯然使用餓漢模式來避免資源(例如鎖)競(jìng)爭(zhēng),提高響應(yīng)速度更好。
5.2 懶漢模式
如果單例對(duì)象構(gòu)造十分耗時(shí)或者占用很多資源,比如加載插件, 初始化網(wǎng)絡(luò)連接,讀取文件等等操作時(shí),并且有可能該對(duì)象程序運(yùn)行時(shí)不會(huì)用到,那么也要在程序一開始就進(jìn)行初始化,就會(huì)導(dǎo)致程序啟動(dòng)時(shí)非常的緩慢。 所以這種情況使用懶漢模式(延遲加載)更好。
//2、懶漢模式
//比較懶,到有人定義對(duì)象的時(shí)候才創(chuàng)建對(duì)象
//(1) 優(yōu)點(diǎn):第一次使用實(shí)例對(duì)象時(shí),創(chuàng)建對(duì)象。進(jìn)程啟動(dòng)無(wú)負(fù)載。多個(gè)單例實(shí)例啟動(dòng)順序自由控制。
//(2) 缺點(diǎn):復(fù)雜。
namespace lazy
{class Singalton{public:static Singalton* GetInstance(){//這里要使用雙判斷加鎖的方式處理,這樣才能很好地提高效率//這個(gè)if是判斷第一次調(diào)用實(shí)例對(duì)象時(shí)需要先創(chuàng)建對(duì)象if (_pInst == nullptr){//加鎖避免線程安全的問題,兩個(gè)線程同時(shí)進(jìn)入了這里,需要先競(jìng)爭(zhēng)鎖_pmtx->lock();//這個(gè)if是判斷如果兩個(gè)進(jìn)程同時(shí)來到了這里,競(jìng)爭(zhēng)到鎖的進(jìn)程//先來到這個(gè)判斷條件,如果_pInst還是nullptr,說明這是第一次調(diào)用實(shí)例對(duì)象//此時(shí)創(chuàng)建一個(gè)Singalton,后競(jìng)爭(zhēng)到鎖的進(jìn)程在再一次if判斷時(shí)_pInst就不再是nullptr//了,此時(shí)說明單例對(duì)象已經(jīng)存在了,就不會(huì)再創(chuàng)建了if (_pInst == nullptr){_pInst = new Singalton;}_pmtx->unlock();}return _pInst;}//一般情況下,單例對(duì)象不需要釋放,因?yàn)槌绦蛘=Y(jié)束就釋放了,// 并且單例對(duì)象一般也不大,所以可以把析構(gòu)函數(shù)設(shè)置成私有//// 但是有些特殊場(chǎng)景:// 1、中途需要顯式釋放;//2、程序結(jié)束時(shí)需要做一些持久化(把數(shù)據(jù)寫入到文件中);// 所以需要提供一個(gè)顯式調(diào)用的DelInstance函數(shù)(里面封裝析構(gòu)函數(shù))static void DelInstance(){cout << "DelInstance()" << endl;if (_pInst != nullptr){//自定義對(duì)象,調(diào)用_pInst對(duì)象的析構(gòu)函數(shù)delete _pInst;delete _pmtx;}}void Add(const pair<string, string>& val){_um.insert(val);}private://構(gòu)造函數(shù)Singalton(){}//析構(gòu)函數(shù)~Singalton(){//顯式調(diào)用析構(gòu)函數(shù)書寫日志或者持久化(數(shù)據(jù)寫入文件)cout << "~Singalton()" << endl;FILE* fp = fopen("test.txt", "w");for (const auto& e : _um){fputs(e.first.c_str(), fp);fputs(":", fp);fputs(e.second.c_str(), fp);fputs("\n", fp);}}//拷貝構(gòu)造函數(shù),單例模式防拷貝Singalton(const Singalton& sg) = delete;//賦值重載函數(shù)Singalton& operator=(const Singalton& sg) = delete;//相當(dāng)于一個(gè)垃圾回收類class GC{public://用Gc的析構(gòu)函數(shù)管理單例類的析構(gòu)函數(shù),因?yàn)樵赟ingalton中聲明了一個(gè)Gc的靜態(tài)的//成員變量,所以在進(jìn)程結(jié)束的時(shí)候會(huì)調(diào)用析構(gòu)函數(shù),而Gc析構(gòu)函數(shù)又管理著Singalton的// 析構(gòu)函數(shù)DelInstance,所以無(wú)論如何進(jìn)程結(jié)束的時(shí)候都會(huì)自動(dòng)調(diào)用Singalton的析構(gòu)// 函數(shù)的,所以就不存在內(nèi)存泄漏的隱患了//~GC(){DelInstance();}};private://如何保證每次調(diào)用GetInstance函數(shù)的時(shí)候獲取到的_Inst都是同一個(gè)呢?因?yàn)檫@里是運(yùn)行時(shí)//才創(chuàng)建對(duì)象的,所以不能用靜態(tài)的對(duì)象,而這里要獲取到同一個(gè)對(duì)象,所以只能在堆上開辟;//但是這里為什么是用靜態(tài)的呢?因?yàn)镚etInstance是靜態(tài)的,沒有this指針,但是GetInstance//函數(shù)中需要用到_Inst,所以需要這個(gè)_Inst也要設(shè)置為靜態(tài)的,這樣GetInstance函數(shù)才能訪問_Inst//同理_mtx也要是靜態(tài)的static Singalton* _pInst;static mutex* _pmtx;unordered_map<string, string> _um;//在單例類中聲明一個(gè)Gc類型的靜態(tài)的成員變量,在類外定義;該對(duì)象在進(jìn)入main函數(shù)前就已經(jīng)創(chuàng)建好了,//到進(jìn)程結(jié)束時(shí)才調(diào)用析構(gòu)函數(shù)銷毀,說明這個(gè)變量是整個(gè)進(jìn)程都有效的static GC gc;};//必須在類外定義類內(nèi)的靜態(tài)成員變量Singalton* Singalton::_pInst = nullptr;mutex* Singalton::_pmtx = new mutex;Singalton::GC Singalton::gc;
}//GC也可以這樣寫,這樣寫更容易理解,用GC創(chuàng)建一個(gè)全局對(duì)象,進(jìn)程結(jié)束時(shí)GC調(diào)用析構(gòu)函數(shù),
// 進(jìn)而調(diào)用lazy::Singalton::DelInstance()釋放單例對(duì)象,但是遇到多個(gè)單例類就把它們
// 全部放到~GC函數(shù)中即可,上面那種寫法就是把GC定義到單例類內(nèi)部,每一個(gè)GC對(duì)象管理一個(gè)單例類
//class GC
//{
//public:
// ~GC()
// {
// lazy::Singalton::DelInstance();
// }
//};
//GC gc;int main()
{//lazy::Singalton* p1 = lazy::Singalton::GetInstance();//lazy::Singalton* p2 = lazy::Singalton::GetInstance();//lazy::Singalton* p3 = lazy::Singalton::GetInstance();//cout << p1 << endl;//cout << p2 << endl;//cout << p3 << endl;lazy::Singalton* p1 = lazy::Singalton::GetInstance();p1->Add(make_pair("string", "字符串"));p1->Add(make_pair("left", "左邊"));p1->Add(make_pair("right", "右邊"));return 0;
}
以上就是常見的特殊類的設(shè)計(jì),你學(xué)會(huì)了嗎?今天的分享就到這里啦,如果你感覺到有所收獲,那么就點(diǎn)點(diǎn)小心心點(diǎn)點(diǎn)關(guān)注唄,后期還會(huì)持續(xù)更新C++的相關(guān)知識(shí)哦,我們下期見!!!