国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【C++修煉之路】33.特殊類設(shè)計(jì)

這篇具有很好參考價(jià)值的文章主要介紹了【C++修煉之路】33.特殊類設(shè)計(jì)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

【C++修煉之路】33.特殊類設(shè)計(jì),C++,c++,設(shè)計(jì)模式,單例模式
每一個(gè)不曾起舞的日子都是對(duì)生命的辜負(fù)

  • 掌握常見特殊類的設(shè)計(jì)方式

一.設(shè)計(jì)一個(gè)類,不能被拷貝

拷貝只會(huì)放生在兩個(gè)場(chǎng)景中:拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載,因此想要讓一個(gè)類禁止拷貝,只需讓該類不能調(diào)用拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載即可。

  • C++98

    將拷貝構(gòu)造函數(shù)與賦值運(yùn)算符重載只聲明不定義,并且將其訪問(wèn)權(quán)限設(shè)置為私有即可。

class CopyBan
{
  // ...
 
private:
  CopyBan(const CopyBan&);
  CopyBan& operator=(const CopyBan&);
  //...
};

原因:

  1. 設(shè)置成私有:如果只聲明沒(méi)有設(shè)置成private,用戶自己如果在類外定義了,就可以不能禁止拷貝了
  2. 只聲明不定義:不定義是因?yàn)樵摵瘮?shù)根本不會(huì)調(diào)用,定義了其實(shí)也沒(méi)有什么意義,不寫反而還簡(jiǎn)單,而且如果定義了就不會(huì)防止成員函數(shù)內(nèi)部拷貝了。
  • C++11

    C++11擴(kuò)展delete的用法,delete除了釋放new申請(qǐng)的資源外,如果在默認(rèn)成員函數(shù)后跟上=delete,表示讓編譯器刪除掉該默認(rèn)成員函數(shù)。

class CopyBan
{
  // ...
  CopyBan(const CopyBan&)=delete;
  CopyBan& operator=(const CopyBan&)=delete;
  //...
};

二.設(shè)計(jì)一個(gè)類,只能在堆上創(chuàng)建對(duì)象

1. 普通類的創(chuàng)建對(duì)象

普通的類,可以在三種位置上創(chuàng)建對(duì)象:

  1. 靜態(tài)區(qū)
#include<iostream>
using namespace std;
class HeapOnly
{};
int main()
{
	HeapOnly hp1;//棧
	HeapOnly* php2 = new HeapOnly;//堆
	static HeapOnly hp3;//靜態(tài)區(qū)
	return 0;
}

2.只能在堆上創(chuàng)建對(duì)象的類

要想只能在堆上創(chuàng)建對(duì)象,那一定需要在構(gòu)造函數(shù)上動(dòng)手腳,因?yàn)闃?gòu)造函數(shù)默認(rèn)在棧上創(chuàng)建對(duì)象。

實(shí)現(xiàn)方式:

  1. 將類的構(gòu)造函數(shù)私有,拷貝構(gòu)造聲明成私有。防止別人調(diào)用拷貝在棧上生成對(duì)象。
  2. 提供一個(gè)靜態(tài)的成員函數(shù),在該靜態(tài)成員函數(shù)中完成堆對(duì)象的創(chuàng)建。
#include<iostream>
using namespace std;
class HeapOnly
{
public:
	static HeapOnly* CreateObject()
	{
		return new HeapOnly;
	}
private:
	HeapOnly()
    {}
};

int main()
{
	HeapOnly* php = HeapOnly::CreateObject();
	return 0;
}

為什么要加上static?

如果CreateObject不加上static,那么在調(diào)用該方法就需要在存在對(duì)象的基礎(chǔ)上才能使用該方法,而該對(duì)象默認(rèn)一定會(huì)用構(gòu)造函數(shù),但是構(gòu)造函數(shù)已經(jīng)私有化,這就是一個(gè)先有雞還是先有蛋的問(wèn)題,因此一定要加上static。

但是就目前的情況,仍然可能在棧上開辟對(duì)象,首先友元一定是可以的。其次,拷貝構(gòu)造函數(shù)沒(méi)有顯示化調(diào)用會(huì)默認(rèn)生成,因此,如下方式仍可以在棧上創(chuàng)建對(duì)象:

int main()
{
	HeapOnly* php2 = HeapOnly::CreateObject();
	HeapOnly php3(*php2);//棧上創(chuàng)建對(duì)象
	return 0;
}

所以,拷貝構(gòu)造函數(shù)同樣需要禁掉,才是只能在堆上創(chuàng)建的類:

#include<iostream>
using namespace std;
class HeapOnly
{
public:
	static HeapOnly* CreateObject()
	{
		return new HeapOnly;
	}
private:
	HeapOnly() {}
	HeapOnly(const HeapOnly&) = delete;
};

int main()
{
	HeapOnly* php2 = HeapOnly::CreateObject();
	return 0;
}

只在堆上創(chuàng)建類的第二種方式:析構(gòu)私有化

如果析構(gòu)私有化,那么直接創(chuàng)建對(duì)象會(huì)顯示沒(méi)有合適的構(gòu)造函數(shù),從而無(wú)法在棧上創(chuàng)建對(duì)象。

class HeapOnly
{
public:
	HeapOnly()
	{}
private:
	~HeapOnly()
	{}

	HeapOnly(const HeapOnly&) = delete;
};

int main()
{
	HeapOnly hp1;
	return 0;
}

【C++修煉之路】33.特殊類設(shè)計(jì),C++,c++,設(shè)計(jì)模式,單例模式

但此時(shí)可以在堆上創(chuàng)建,那么此時(shí)分為如下步驟:

  1. 析構(gòu)函數(shù)私有化
  2. 構(gòu)造函數(shù)public顯示調(diào)用
  3. 新增Destory方法,用來(lái)釋放堆空間
class HeapOnly
{
public:
	HeapOnly()
	{}
	void Destory()
	{
		this->~HeapOnly();
	}
private:
	~HeapOnly()
	{}

	HeapOnly(const HeapOnly&) = delete;
};

int main()
{
	HeapOnly* php1 = new HeapOnly;
	php1->Destory();

	return 0;
}

【C++修煉之路】33.特殊類設(shè)計(jì),C++,c++,設(shè)計(jì)模式,單例模式

Destory對(duì)于static沒(méi)有要求,用不用static修飾完全是我們自己所決定的。

注:在vs2019中,上面的this必須顯示調(diào)用才沒(méi)有錯(cuò)誤。

三.設(shè)計(jì)一個(gè)類,只能在棧上創(chuàng)建對(duì)象

方法一:(同上)

  1. 將構(gòu)造函數(shù)私有化。
  2. 然后設(shè)計(jì)靜態(tài)方法創(chuàng)建對(duì)象返回即可。
//請(qǐng)?jiān)O(shè)計(jì)一個(gè)類,只能在棧上創(chuàng)建對(duì)象
class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		return StackOnly();
	}
private:
	StackOnly()
	{}

};
int main()
{
	StackOnly so1 = StackOnly::CreateObj();
	// 下面兩種靜態(tài)區(qū)和堆的位置都不能創(chuàng)建
	//static StackOnly so2;
	//StackOnly* pso3 = new StackOnly;
	return 0;
}

實(shí)際上,這種方法也沒(méi)有徹底的封死,下面這種方式仍然可以在靜態(tài)區(qū)創(chuàng)建:

int main()
{
	static StackOnly so2 = StackOnly::CreateObj();
	return 0;
}

【C++修煉之路】33.特殊類設(shè)計(jì),C++,c++,設(shè)計(jì)模式,單例模式

解決這個(gè)問(wèn)題的方式:

這里設(shè)計(jì)到的強(qiáng)制類型轉(zhuǎn)換,強(qiáng)制類型轉(zhuǎn)換中間會(huì)生成一個(gè)臨時(shí)對(duì)象,將這個(gè)臨時(shí)對(duì)象拷貝給需要定義的對(duì)象,若把拷貝構(gòu)造封住,那么不僅這個(gè)會(huì)報(bào)錯(cuò),前面的也會(huì)報(bào)錯(cuò),因?yàn)榍罢叩馁x值也是將返回的對(duì)象臨時(shí)拷貝:

【C++修煉之路】33.特殊類設(shè)計(jì),C++,c++,設(shè)計(jì)模式,單例模式

因此,沒(méi)有什么很好的辦法去完全的封死。但硬要封死,即把拷貝構(gòu)造封住,那就不要用 = 獲取,而是直接調(diào)用,如下:

//請(qǐng)?jiān)O(shè)計(jì)一個(gè)類,只能在棧上創(chuàng)建對(duì)象
class StackOnly
{
public:
	static StackOnly&& CreateObj()
	{
		return StackOnly();
	}

	void Print() const
	{
		cout << "StackOnly::Print()" << endl;
	}
private:
	StackOnly()
	{}
	StackOnly(const StackOnly&) = delete;

};
int main()
{
	/*StackOnly so1 = StackOnly::CreateObj();
	static StackOnly so2 = StackOnly::CreateObj();*/

	StackOnly::CreateObj().Print();
	const StackOnly& so4 = StackOnly::CreateObj();
	so4.Print();
	return 0;
}

目的就是防止拷貝。

ps,由于StackOnly()是局部對(duì)象,出了作用域被銷毀,因此采用右值引用才可以傳出。


方法二:封注operator newoperator delete

class StackOnly
{
public:
    StackOnly()
	{}
	static StackOnly CreateObj()
	{
		return StackOnly();
	}
	void* operator new(size_t size) = delete;
	void operator delete(void* p) = delete;
	
};

這種方式同樣可以,因?yàn)樵趎ew對(duì)象的過(guò)程中,一定會(huì)存在operator new的步驟。但是這種方法只能封住堆上的,卻無(wú)法封住靜態(tài)的。

所以最好的方式就是用方式一。

四.設(shè)計(jì)一個(gè)類,不能被繼承

  • C++98方式
// C++98中構(gòu)造函數(shù)私有化,派生類中調(diào)不到基類的構(gòu)造函數(shù)。則無(wú)法繼承
class NonInherit
{
public:
    static NonInherit GetInstance()
    {
        return NonInherit();
    }
private:
    NonInherit()
    {}
};
  • C++11方法

final關(guān)鍵字,final修飾類,表示該類不能被繼承。

class A  final
{
  // ....
};

五.單例模式

  • 單例模式:只能創(chuàng)建一個(gè)對(duì)象。

1.什么是設(shè)計(jì)模式?

設(shè)計(jì)模式是在軟件工程中經(jīng)過(guò)反復(fù)實(shí)踐證明的一套解決問(wèn)題的經(jīng)驗(yàn)總結(jié),用于解決常見的設(shè)計(jì)問(wèn)題。以下是一些常見的設(shè)計(jì)模式:

  1. 創(chuàng)建型模式(Creational Patterns):

    • 工廠方法模式(Factory Method Pattern)
    • 抽象工廠模式(Abstract Factory Pattern)
    • 單例模式(Singleton Pattern)
    • 建造者模式(Builder Pattern)
    • 原型模式(Prototype Pattern)
  2. 結(jié)構(gòu)型模式(Structural Patterns):

    • 適配器模式(Adapter Pattern)
    • 橋接模式(Bridge Pattern)
    • 組合模式(Composite Pattern)
    • 裝飾者模式(Decorator Pattern)
    • 外觀模式(Facade Pattern)
    • 享元模式(Flyweight Pattern)
    • 代理模式(Proxy Pattern)
  3. 行為型模式(Behavioral Patterns):

    • 觀察者模式(Observer Pattern)
    • 狀態(tài)模式(State Pattern)
    • 策略模式(Strategy Pattern)
    • 命令模式(Command Pattern)
    • 職責(zé)鏈模式(Chain of Responsibility Pattern)
    • 迭代器模式(Iterator Pattern)
    • 中介者模式(Mediator Pattern)
    • 備忘錄模式(Memento Pattern)
    • 訪問(wèn)者模式(Visitor Pattern)
    • 模板方法模式(Template Method Pattern)
  4. 并發(fā)型模式(Concurrent Patterns):

    • 信號(hào)量模式(Semaphore Pattern)
    • 線程池模式(Thread Pool Pattern)
    • 讀寫鎖模式(Read-Write Lock Pattern)
    • 生產(chǎn)者消費(fèi)者模式(Producer-Consumer Pattern)

以上僅是一些常見的設(shè)計(jì)模式,實(shí)際上還有其他的設(shè)計(jì)模式。每個(gè)設(shè)計(jì)模式都有特定的應(yīng)用場(chǎng)景和解決問(wèn)題的方式。請(qǐng)注意,在使用設(shè)計(jì)模式時(shí),應(yīng)根據(jù)具體的需求和情況來(lái)選擇適當(dāng)?shù)脑O(shè)計(jì)模式。

比如迭代器模式,把復(fù)雜的東西給封裝好,使用時(shí)就可以避免接觸復(fù)雜的底層結(jié)構(gòu)。

比如配接器模式等等,也是這個(gè)意思。

使用設(shè)計(jì)模式的目的: 為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。 設(shè)計(jì)模式使代碼編寫真正工程化;設(shè)計(jì)模式是軟件工程的基石脈絡(luò),如同大廈的結(jié)構(gòu)一樣。

2.單例模式

一個(gè)類只能創(chuàng)建一個(gè)對(duì)象,即單例模式,該模式可以保證系統(tǒng)中該類只有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn),該實(shí)例被所有程序模塊共享。比如在某個(gè)服務(wù)器程序中,該服務(wù)器的配置信息存放在一個(gè)文件中,這些配置數(shù)據(jù)由一個(gè)單例對(duì)象統(tǒng)一讀取,然后服務(wù)進(jìn)程中的其他對(duì)象再通過(guò)這個(gè)單例對(duì)象獲取這些配置信息,這種方式簡(jiǎn)化了在復(fù)雜環(huán)境下的配置管理。

由于全局對(duì)象只能有一個(gè),換句話說(shuō)是獲取這個(gè)對(duì)象,那就需要對(duì)構(gòu)造函數(shù)進(jìn)行操作。

單例模式有兩種實(shí)現(xiàn)模式:餓漢模式、懶漢模式

餓漢模式:不管你將來(lái)用不用,程序啟動(dòng)時(shí)就創(chuàng)建一個(gè)唯一的實(shí)例對(duì)象。

餓漢模式的條件:main函數(shù)之前就初始化

設(shè)計(jì)餓漢模式的步驟:

  1. 將構(gòu)造函數(shù)設(shè)成private,以及封死拷貝構(gòu)造和重載賦值
  2. 定義成員變量,變量類型為static 類型名
  3. 在類外初始化這個(gè)單例的對(duì)象
  4. 添加其它成員方法
//單例模式的類:全局只有一個(gè)唯一對(duì)象
// 餓漢模式(main函數(shù)之前初始化)
// 缺點(diǎn):1、單例對(duì)象初始化時(shí)對(duì)象太多,導(dǎo)致啟動(dòng)慢 
//		 2、多個(gè)單例類有初始化依賴關(guān)系,餓漢模式無(wú)法控制
class InfoSingleton
{
public:
	static InfoSingleton& GetInstance()
	{
		return _sins;
	}

	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
private:
	InfoSingleton()
	{}
	InfoSingleton(const InfoSingleton&) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;
	map<string, int> _info;
	// ...
private:
	static InfoSingleton _sins;
};

InfoSingleton InfoSingleton::_sins;

int main()
{
	InfoSingleton::GetInstance().Insert("張三", 10000);
	InfoSingleton& infosl = InfoSingleton::GetInstance();
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("趙六", 11000);

	infosl.Print();
	cout << endl;
	InfoSingleton::GetInstance().Insert("張三", 18000);
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("趙六", 11000);
	infosl.Print();
	return 0;
}

【C++修煉之路】33.特殊類設(shè)計(jì),C++,c++,設(shè)計(jì)模式,單例模式

可見在調(diào)用時(shí)可以通過(guò)引用來(lái)簡(jiǎn)化代碼量。

餓漢模式的缺點(diǎn):

  1. 單例對(duì)象初始化數(shù)據(jù)太多,導(dǎo)致啟動(dòng)慢
  2. 多個(gè)單例類有初始化依賴關(guān)系,餓漢模式無(wú)法控制

假設(shè)有兩個(gè)單例類A和B,分別代表數(shù)據(jù)庫(kù)和文件系統(tǒng),要求先初始化A,再初始化B,并且B會(huì)依賴A,那么此時(shí)餓漢模式就無(wú)法控制順序。

如果這個(gè)單例對(duì)象在多線程高并發(fā)環(huán)境下頻繁使用,性能要求較高,那么顯然使用餓漢模式來(lái)避免資源競(jìng)爭(zhēng),提高響應(yīng)速度更好。

懶漢模式

如果單例對(duì)象構(gòu)造十分耗時(shí)或者占用很多資源,比如加載插件啊, 初始化網(wǎng)絡(luò)連接啊,讀取文件啊等等,而有可能該對(duì)象程序運(yùn)行時(shí)不會(huì)用到,那么也要在程序一開始就進(jìn)行初始化,就會(huì)導(dǎo)致程序啟動(dòng)時(shí)非常的緩慢。 所以這種情況使用懶漢模式(延遲加載)更好。

懶漢模式的條件:

  1. 對(duì)象在main函數(shù)之后才會(huì)創(chuàng)建,不會(huì)影響啟動(dòng)順序
  2. 可以主動(dòng)控制創(chuàng)建順序

設(shè)計(jì)懶漢模式的步驟:(與餓漢模式基本相同)

  1. 將構(gòu)造函數(shù)設(shè)成private,以及封死拷貝構(gòu)造和重載賦值
  2. 定義成員變量,變量類型為static 類型名
  3. 在類外初始化這個(gè)單例的對(duì)象
  4. 添加其它成員方法

與餓漢模式的區(qū)別:

  1. 對(duì)象在main函數(shù)之后才會(huì)創(chuàng)建,不會(huì)影響啟動(dòng)順序
  2. 可以主動(dòng)控制創(chuàng)建順序
  3. 將對(duì)象的創(chuàng)建改為在堆上創(chuàng)建
  4. 懶漢模式存在多個(gè)對(duì)象一起調(diào)用GetInstance的情況,存在線程安全的風(fēng)險(xiǎn),可能new出來(lái)多個(gè)對(duì)象,因此需要加鎖,需要新增一個(gè)鎖的成員對(duì)象,并定義為static類型;餓漢模式一開始就一個(gè)對(duì)象,不用創(chuàng)建,所以不用鎖。

注意:鎖不能被拷貝,因此定義鎖的成員變量時(shí)可以用指針(地址)或者引用的方式定義,C++采用地址的行為不常見,用引用更好。

加鎖也是有講究的,如果像這樣的代碼:

//多個(gè)對(duì)象一起調(diào)用GetInstance,存在線程安全的風(fēng)險(xiǎn),可能new出來(lái)多個(gè)對(duì)象,因此需要加鎖
static InfoSingleton& GetInstance()
{
    _pmtx.lock();
    if (_psins == nullptr)//避免對(duì)象new出來(lái)以后每次都加鎖,提高性能
    {
        _psins = new InfoSingleton;
    }
    _pmtx.unlock();
    return *_psins;
}

由于這種方式每次都需要加鎖,但實(shí)際上只有第一次創(chuàng)建對(duì)象才需要加鎖,所以為了避免鎖影響效率,使用雙層if檢查;此外,對(duì)于new,一旦拋異常,就需要捕獲,此時(shí)可以使用try-catch,但這種寫法不可行,通過(guò)之前智能指針的RAII思想,我們可以自己設(shè)定一個(gè)類:基于RAII思想的管理類,來(lái)防止鎖的問(wèn)題。

為什么try-catch不可行,因?yàn)檫€在加鎖階段,一旦進(jìn)行捕獲跳轉(zhuǎn),那么這把鎖會(huì)一直鎖住,為了避免出現(xiàn)這種情況,才使用RAII的思想。C++線程庫(kù)中也有對(duì)應(yīng)的庫(kù)函數(shù)方法,但是這里仍然可以手撕一個(gè)。

//RAII的鎖管理類
template<class Lock>
class LockGuard
{
public:
	LockGuard(Lock& lk)
		:_lk(lk)
	{
		lk.lock();
	}

	~LockGuard()
	{
		_lk.unlock();
	}
private:
	Lock& _lk;//成員變量用引用-->避免拷貝
};

//懶漢模式
//1、對(duì)象在main函數(shù)之后才會(huì)創(chuàng)建,不會(huì)影響啟動(dòng)順序
//2、可以主動(dòng)控制創(chuàng)建順序

//問(wèn)題:
class InfoSingleton
{
public:
	//多個(gè)對(duì)象一起調(diào)用GetInstance,存在線程安全的風(fēng)險(xiǎn),可能new出來(lái)多個(gè)對(duì)象,因此需要加鎖
	static InfoSingleton& GetInstance()
	{
		//第一次獲取單例對(duì)象的時(shí)候創(chuàng)建對(duì)象
		//雙檢查加鎖
		if (_psins == nullptr)//避免對(duì)象new出來(lái)以后每次都加鎖,提高性能
		{
			// t1  t2
			LockGuard<mutex> mtx(_smtx);
			if (_psins == nullptr) //保證線程安全且只new一次
			{
				_psins = new InfoSingleton;
			}
		}

		return *_psins;
	}

	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
private:
	InfoSingleton()
	{}
	InfoSingleton(const InfoSingleton&) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;
	map<string, int> _info;
	// ...
private:
	static InfoSingleton* _psins;
	static mutex _smtx;
};

InfoSingleton* InfoSingleton::_psins = nullptr;
mutex InfoSingleton::_smtx;

int main()
{
	InfoSingleton::GetInstance().Insert("張三", 10000);
	InfoSingleton& infosl = InfoSingleton::GetInstance();
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("趙六", 11000);

	infosl.Print();
	cout << endl;
	InfoSingleton::GetInstance().Insert("張三", 18000);
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("趙六", 11000);
	infosl.Print();
	return 0;
}

【C++修煉之路】33.特殊類設(shè)計(jì),C++,c++,設(shè)計(jì)模式,單例模式

庫(kù)中也有對(duì)應(yīng)的加鎖方法:

static InfoSingleton& GetInstance()
{
    //第一次獲取單例對(duì)象的時(shí)候創(chuàng)建對(duì)象
    //雙檢查加鎖
    if (_psins == nullptr)//避免對(duì)象new出來(lái)以后每次都加鎖,提高性能
    {
        // t1  t2
        //LockGuard<mutex> mtx(_smtx);
        std::lock_guard<mutex> lock(_smtx);//庫(kù)中的方法
        if (_psins == nullptr) //保證線程安全且只new一次
        {
            _psins = new InfoSingleton;
        }
    }

    return *_psins;
}

new出來(lái)之后是否需要釋放?

一般單例模式對(duì)象不需要考慮釋放。單例模式的類的一個(gè)對(duì)象通常在整個(gè)程序運(yùn)行期間都會(huì)使用,因此最后不delete也不會(huì)有問(wèn)題,只要進(jìn)程最終正常結(jié)束,對(duì)象的資源就會(huì)由OS自動(dòng)釋放。

什么時(shí)候單例模式的對(duì)象需要釋放?

單例對(duì)象不用時(shí),必須手動(dòng)處理,一些資源需要保存。假設(shè)工資名單需要保存到文件里,要求系統(tǒng)結(jié)束之前將信息保存進(jìn)去,此時(shí)就需要手動(dòng)處理。所以,可以新增一個(gè)方法DelInstance(),是否需要調(diào)用取決于自己:

//懶漢模式
//1、對(duì)象在main函數(shù)之后才會(huì)創(chuàng)建,不會(huì)影響啟動(dòng)順序
//2、可以主動(dòng)控制創(chuàng)建順序
class InfoSingleton
{
public:
	//多個(gè)對(duì)象一起調(diào)用GetInstance,存在線程安全的風(fēng)險(xiǎn),可能new出來(lái)多個(gè)對(duì)象,因此需要加鎖
	static InfoSingleton& GetInstance()
	{
		//第一次獲取單例對(duì)象的時(shí)候創(chuàng)建對(duì)象
		//雙檢查加鎖
		if (_psins == nullptr)//避免對(duì)象new出來(lái)以后每次都加鎖,提高性能
		{
			// t1  t2
			//LockGuard<mutex> mtx(_smtx);
			std::lock_guard<mutex> lock(_smtx);
			if (_psins == nullptr) //保證線程安全且只new一次
			{
				_psins = new InfoSingleton;
			}
		}

		return *_psins;
	}

	//一般單例對(duì)象不需要考慮釋放
	//單例對(duì)象不用時(shí),必須手動(dòng)處理,一些資源需要保存
	static void DelInstance()
	{
		//保存數(shù)據(jù)到文件
		// ...
		std::lock_guard<mutex> lock(_smtx);
		if (_psins)
		{
			delete _psins;
			_psins = nullptr;
		}
	}

	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
private:
	InfoSingleton()
	{}
	InfoSingleton(const InfoSingleton&) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;
	map<string, int> _info;
	// ...
private:
	static InfoSingleton* _psins;
	static mutex _smtx;
};

InfoSingleton* InfoSingleton::_psins = nullptr;
mutex InfoSingleton::_smtx;

int main()
{
	InfoSingleton::GetInstance().Insert("張三", 10000);
	InfoSingleton& infosl = InfoSingleton::GetInstance();
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("趙六", 11000);

	infosl.Print();
	cout << endl;
	InfoSingleton::GetInstance().Insert("張三", 18000);
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("趙六", 11000);
	infosl.Print();

	InfoSingleton::DelInstance();//主動(dòng)調(diào)用
	return 0;
}

如果忘記主動(dòng)調(diào)用,同樣會(huì)產(chǎn)生錯(cuò)誤,因此仍需要設(shè)計(jì)一個(gè)能夠自動(dòng)回收的方式,這里采用新增一個(gè)內(nèi)部類GC,利用RAII的思想,一旦忘主動(dòng)回收,其在main函數(shù)結(jié)束時(shí)就會(huì)自動(dòng)回收,此時(shí)就需要新增一個(gè)成員變量以及內(nèi)部類:

注:內(nèi)部類是外部類的友元

//懶漢模式
//1、對(duì)象在main函數(shù)之后才會(huì)創(chuàng)建,不會(huì)影響啟動(dòng)順序
//2、可以主動(dòng)控制創(chuàng)建順序
class InfoSingleton
{
public:
	//多個(gè)對(duì)象一起調(diào)用GetInstance,存在線程安全的風(fēng)險(xiǎn),可能new出來(lái)多個(gè)對(duì)象,因此需要加鎖
	static InfoSingleton& GetInstance()
	{
		//第一次獲取單例對(duì)象的時(shí)候創(chuàng)建對(duì)象
		//雙檢查加鎖
		if (_psins == nullptr)//避免對(duì)象new出來(lái)以后每次都加鎖,提高性能
		{
			// t1  t2
			//LockGuard<mutex> mtx(_smtx);
			std::lock_guard<mutex> lock(_smtx);
			if (_psins == nullptr) //保證線程安全且只new一次
			{
				_psins = new InfoSingleton;
			}
		}

		return *_psins;
	}

	//一般單例對(duì)象不需要考慮釋放
	//單例對(duì)象不用時(shí),必須手動(dòng)處理,一些資源需要保存
	static void DelInstance()
	{
		//保存數(shù)據(jù)到文件
		// ...
		std::lock_guard<mutex> lock(_smtx);
		if (_psins)
		{
			delete _psins;
			_psins = nullptr;
		}
	}
	//忘記調(diào)用DelInstance(),自動(dòng)回收
	class GC
	{
	public:
		~GC()
		{
			if (_psins)
			{
				cout << " ~GC()" << endl;
				DelInstance();
			}
		}
	};

	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
private:
	InfoSingleton()
	{}
	InfoSingleton(const InfoSingleton&) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;
	map<string, int> _info;
	// ...
private:
	static InfoSingleton* _psins;
	static mutex _smtx;
	static GC _gc;
};

InfoSingleton* InfoSingleton::_psins = nullptr;
mutex InfoSingleton::_smtx;
InfoSingleton::GC InfoSingleton::_gc;

int main()
{
	InfoSingleton::GetInstance().Insert("張三", 10000);
	InfoSingleton& infosl = InfoSingleton::GetInstance();
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("趙六", 11000);

	infosl.Print();
	cout << endl;
	InfoSingleton::GetInstance().Insert("張三", 18000);
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("趙六", 11000);
	infosl.Print();
	return 0;
}

【C++修煉之路】33.特殊類設(shè)計(jì),C++,c++,設(shè)計(jì)模式,單例模式

因此,可以主動(dòng)回收,也可以在程序結(jié)束時(shí)自動(dòng)回收,但單例對(duì)象一般不需要回收。

實(shí)現(xiàn)懶漢的另一種方式:

懶漢模式的實(shí)現(xiàn)還有另一種方式,直接static,也是只創(chuàng)建一次對(duì)象,所以下面的方式也可以,但不是一種通用的方式。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-538798.html

//是懶漢:因?yàn)殪o態(tài)的局部變量是在main函數(shù)之后才創(chuàng)建初始化的:局部靜態(tài)變量的初始化只初始化一次。
//C++11之前,不能保證sinst的初始化是線程安全的。
//C++11之后,可以。
class InfoSingleton
{
public:
	//多個(gè)對(duì)象一起調(diào)用GetInstance,存在線程安全的風(fēng)險(xiǎn),可能new出來(lái)多個(gè)對(duì)象,因此需要加鎖
	static InfoSingleton& GetInstance()
	{
		static InfoSingleton sinst;
		return sinst;
	}


	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
private:
	InfoSingleton()
	{
		cout << "InfoSingleton()" << endl;
	}
	InfoSingleton(const InfoSingleton&) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;
	map<string, int> _info;
	// ...
private:
};

int main()
{
	InfoSingleton::GetInstance().Insert("張三", 10000);
	InfoSingleton& infosl = InfoSingleton::GetInstance();
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("趙六", 11000);

	infosl.Print();
	cout << endl;
	InfoSingleton::GetInstance().Insert("張三", 18000);
	infosl.Insert("李四", 12000);
	infosl.Insert("王五", 15000);
	infosl.Insert("趙六", 11000);
	infosl.Print();
	return 0;
}

到了這里,關(guān)于【C++修煉之路】33.特殊類設(shè)計(jì)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • C++中特殊類的設(shè)計(jì)與單例模式的簡(jiǎn)易實(shí)現(xiàn)

    C++中特殊類的設(shè)計(jì)與單例模式的簡(jiǎn)易實(shí)現(xiàn)

    對(duì)于這種特殊類的設(shè)計(jì)我們一般都是優(yōu)先考慮私有構(gòu)造函數(shù)。 然后對(duì)于一些特殊要求就直接通過(guò)靜態(tài)成員函數(shù)的實(shí)現(xiàn)來(lái)完成。 ?這里選擇禁掉拷貝構(gòu)造函數(shù)和拷貝函數(shù)是為了防止將已創(chuàng)建的對(duì)象去拷貝構(gòu)造新的對(duì)象。 ?這里如果沒(méi)有禁掉operator new和operator delete的話就會(huì)導(dǎo)致以

    2024年01月18日
    瀏覽(28)
  • 從C語(yǔ)言到C++_37(特殊類設(shè)計(jì)和C++類型轉(zhuǎn)換)單例模式

    從C語(yǔ)言到C++_37(特殊類設(shè)計(jì)和C++類型轉(zhuǎn)換)單例模式

    目錄 1. 特殊類設(shè)計(jì) 1.1 不能被拷貝的類 1.2 只能在堆上創(chuàng)建的類 1.3 只能在棧上創(chuàng)建的類 1.4 不能被繼承的類 1.5 只能創(chuàng)建一個(gè)對(duì)象的類(單例模式)(重點(diǎn)) 1.5.1 餓漢模式 1.5.2?懶漢模式 2.?類型轉(zhuǎn)換 2.1 static_cast 2.2 reinterpret_cast 2.3 const_cast 2.4 dynamic_cast 3. RTTI(了解)和類型轉(zhuǎn)換常見面

    2024年02月10日
    瀏覽(26)
  • 【重點(diǎn):?jiǎn)卫J健刻厥忸愒O(shè)計(jì)

    【重點(diǎn):?jiǎn)卫J健刻厥忸愒O(shè)計(jì)

    方式如下: 將構(gòu)造函數(shù)設(shè)置為私有,防止外部直接調(diào)用構(gòu)造函數(shù)在棧上創(chuàng)建對(duì)象。 向外部提供一個(gè)獲取對(duì)象的static接口,該接口在堆上創(chuàng)建一個(gè)對(duì)象并返回。 將拷貝構(gòu)造函數(shù)設(shè)置為私有,并且只聲明不實(shí)現(xiàn),防止外部調(diào)用拷貝構(gòu)造函數(shù)在棧上創(chuàng)建對(duì)象。 說(shuō)明一下: 向外部

    2024年02月15日
    瀏覽(22)
  • 特殊類的設(shè)計(jì)(含單例模式)

    特殊類的設(shè)計(jì)(含單例模式)

    拷貝只會(huì)放生在兩個(gè)場(chǎng)景中:拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載,因此 想要讓一個(gè)類禁止拷貝,只需讓該類不能調(diào)用拷貝構(gòu)造函數(shù)以及賦值運(yùn)算符重載即可。 C++98: 將拷貝構(gòu)造函數(shù)與賦值運(yùn)算符重載只聲明不定義,并且將其訪問(wèn)權(quán)限設(shè)置為私有即可。 原因: 設(shè)置成私有:如

    2024年01月23日
    瀏覽(14)
  • 【C++】設(shè)計(jì)模式-單例模式

    【C++】設(shè)計(jì)模式-單例模式

    目錄 一、單例模式 單例模式的三個(gè)要點(diǎn) 針對(duì)上述三要點(diǎn)的解決方案 常用的兩類單例模式 ?二、懶漢模式實(shí)現(xiàn) 1.基本實(shí)現(xiàn) 2.鎖+靜態(tài)成員析構(gòu)單例 3.雙層檢查鎖定優(yōu)化 4.雙層檢查鎖定+智能指針 三、餓漢模式實(shí)現(xiàn) 1.基礎(chǔ)實(shí)現(xiàn) 2.嵌套內(nèi)部類解決內(nèi)存泄漏 3.智能指針解決內(nèi)存泄漏

    2024年02月16日
    瀏覽(16)
  • C++設(shè)計(jì)模式:?jiǎn)卫J剑ㄊ? decoding=
  • 【設(shè)計(jì)模式】C++單例模式詳解

    ?個(gè)類僅有?個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn),該實(shí)例被所有程序模塊共享。 那么,我們必須保證: 該類不能被復(fù)制;也不能被公開的創(chuàng)造。 對(duì)于 C++ 來(lái)說(shuō),它的構(gòu)造函數(shù),拷貝構(gòu)造函數(shù)和賦值函數(shù)都不能被公開調(diào)用。 單例模式又分為 懶漢模式 和 餓漢模式 ,它們

    2024年02月05日
    瀏覽(23)
  • C++設(shè)計(jì)模式代碼--單例模式

    參考:5. 單例模式(Singleton) (yuque.com) 1、什么是單例模式 保證一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)該實(shí)例的全局節(jié)點(diǎn); 2、什么情況下需要單例模式 某個(gè)類的對(duì)象在軟件運(yùn)行之初就創(chuàng)建,并且在軟件的很多地方都需要讀寫這個(gè)類的信息;使用單例模式的話,類對(duì)象就只要

    2024年02月03日
    瀏覽(31)
  • c++設(shè)計(jì)模式之單例模式

    一個(gè)類無(wú)論創(chuàng)建多少對(duì)象 , 都只能得到一個(gè)實(shí)例 如上述代碼中,我們通過(guò)new運(yùn)算符創(chuàng)建出了類A的三個(gè)對(duì)象實(shí)例,而我們現(xiàn)在要做的是,如何設(shè)計(jì)類A,使得上述代碼運(yùn)行之后永遠(yuǎn)只產(chǎn)生同一個(gè)對(duì)象實(shí)例 ????????我們知道,一個(gè)類對(duì)象是通過(guò)這個(gè)類的構(gòu)造函數(shù)創(chuàng)建的,因此

    2024年01月19日
    瀏覽(26)
  • C++設(shè)計(jì)模式創(chuàng)建型之單例模式

    一、概述 ? ? ? ? 單例模式也稱單態(tài)模式,是一種創(chuàng)建型模式,用于創(chuàng)建只能產(chǎn)生一個(gè)對(duì)象實(shí)例的類。例如,項(xiàng)目中只存在一個(gè)聲音管理系統(tǒng)、一個(gè)配置系統(tǒng)、一個(gè)文件管理系統(tǒng)、一個(gè)日志系統(tǒng)等,甚至如果吧整個(gè)Windows操作系統(tǒng)看成一個(gè)項(xiàng)目,那么其中只存在一個(gè)任務(wù)管理

    2024年02月14日
    瀏覽(29)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包