C++之设计模式

1.概要

设计模式是一套被反复使用的代码设计经验的总结,是经过提炼的出色设计方法。设计模式主要是为了解决某类重复出现的问题而出现的一套成功有效的解决方案,设计模式的出现提高代码复用性、扩展性、可维护性、灵活性、稳健性以及安全可靠性的解决方案。

2.分类

创建型模式、结构型模式、行为型模式

3.单例模式

属于创建型模式,三个要点:

1.当前类最多只能创建一个实例

2.当前这个唯一的实例,必须由当前类创建(自主创建),而不是调用者创建

3.必须向整个系统提供全局的访问点来获取唯一的实例

一个好的单例应具备以下特点:

1.全局只有一个实例,同时禁止用户自己声明和定义实例(方法:构造函数私有化)

2.线程安全

3.禁止复制和拷贝

4.用户通过接口获得实例(方法:使用static类成员函数)

3.1懒汉式

懒汉式的方法是直到使用时才实例化对象,也就说直到调用Instance() 方法的时候才 new 一个单例的对象, 如果不被调用就不会占用内存。是一种“时间换空间”的做法。如果单线程没有问题,当多线程的时候就会出现不可靠的情况。

#include<iostream>
using namespace std;

//if判断:保证创建唯一实例
//构造函数私有化:防止调用者自己在类外创建实例
//static类成员函数:提供接口

class CSingleton {
	CSingleton(){}
	CSingleton(const CSingleton&) = delete;//弃用
	CSingleton& operator = (const CSingleton&) = delete;
	~CSingleton() {}
	static CSingleton* m_pSin;
public:
	//有问题的代码
	static CSingleton* GetSingleton() {
		//加锁
		if (!m_pSin)
		{
			m_pSin = new CSingleton;
		}
		//解锁
		return m_pSin;
	}
};
CSingleton* CSingleton::m_pSin = nullptr;
int main()
{
	CSingleton* m_pSin1 = CSingleton::GetSingleton();
	CSingleton* m_pSin2 = CSingleton::GetSingleton();
	cout << m_pSin1 << " " << m_pSin2 << endl;//00B49750 00B49750
}

 以上代码存在的问题:

1.线程安全的问题:当多线程获取单例时有可能引发竞态条件:第一个线程在if中判断m_pSin是空的,于是开始实例化单例;同时第二个线程也尝试获取单例,这个时候判断m_pSin还是空的,于是也开始实例化单例;这样就会实例化出两个对象,这就是线程安全问题的由来。

解决办法:

1.加锁

class CSingleton {
	CSingleton(){}
	CSingleton(const CSingleton&) = delete;//弃用
	CSingleton& operator = (const CSingleton&) = delete;
	static CSingleton* m_pSin;
public:
	//有问题的代码
	static CSingleton* GetSingleton() {
		//加锁
		if (!m_pSin)
		{
			m_pSin = new CSingleton;
		}
		//解锁
		return m_pSin;
	}
};

 2.静态局部变量

多线程下不会失效,编译器创建静态局部变量保证其原子性。(第一次调用GetSingleton()时创建sin对象,在后续调用该函数时不会继续创建)

public:
	static CSingleton* GetSingleton() {
		static CSingleton sin;
		return &sin;
	}
};

2.内存泄漏:注意到类中只负责new出对象,却没有负责delete对象,因此只有构造函数被调用,析构函数却没有被调用,因此会导致内存泄漏。

解决办法:

1.使用自定义函数对内存进行回收,在我们需要回收时进行调用

public:
	static void DestorySingleton(CSingleton*& p) {
		if (p) {
			delete p;
			p = nullptr;
			m_pSin = nullptr;
		}
	}

2.利用静态类对象在程序结束时自动回收,不需要我们手动回收,从而实现对内存的自动回收

public:
	static class DeleteSingleton {
		~DeleteSingleton() {
			if (m_pSin) {
				delete m_pSin;
			}
			m_pSin = nullptr;
		}
	}del;

CSingleton::DeleteSingleton CSingleton::del;

3.2饿汉式 

在程序创建之初自动创建了对象,它是一个全局的对象,生命周期直到程序结束,所以不需要提供销毁对象的方法。是一种“空间换时间”的做法。不存在多线程下的安全问题。

#include<iostream>
using namespace std;

class CSingleton {
	CSingleton(){}
	CSingleton(const CSingleton&) = delete;
	CSingleton& operator = (const CSingleton&) = delete;
	~CSingleton() {
		cout << "调用析构" << endl;
	}
	static CSingleton sin;//静态对象,在编译期就创建好

public:
	static CSingleton* GetSingleton() {
		return &sin;
	}
};
CSingleton CSingleton::sin;
int main()
{
	CSingleton* m_pSin1 = CSingleton::GetSingleton();
	CSingleton* m_pSin2 = CSingleton::GetSingleton();
	cout << m_pSin1 << " " << m_pSin2 << endl;//0009E138 0009E138 调用析构
}

单例模式的优点:

1.单例模式提供了严格的对唯一实例的创建、访问和销毁,安全性高。

2.单例模式的实现可以节省系统资源。

4.工厂模式

属于创建型模式,主要用来集中创建对象的,如果在任何使用的地方创建对象那就造成了类或方法之间的耦合,如果要更换对象那么在所有使用到的地方都要修改一遍,不利于后期的维护,也违背了开闭设计原则,如果使用工厂来创建对象,那么就彻底解耦合了,如果要修改只需要修改工厂类即可。

开闭原则是面向对象设计的一个重要原则,开闭原则的核心思想是:

于扩展:软件实体(类、模块、函数等)应该对扩展开放,意味着当需要增加新功能时,应该能够添加新的代码来扩展现有的代码,而不是修改现有的代码。

于修改:软件实体应该对修改关闭,意味着在添加新功能时,不应该修改现有的代码。这样可以保持现有的代码稳定,减少引入新错误的风险。

工厂模式最大的优势:解耦

#include<iostream>
using namespace std;

class CEngine {
public:
	virtual void working() = 0;
};

class CEngine2L :public CEngine {
public:
	void working() {
		cout << "2.0L发动机正在工作" << endl;
	}
};

class CEngine2T :public CEngine {
public:
	void working() {
		cout << "2.0T发动机正在工作" << endl;
	}
};

class CCar {
public:
	CEngine* m_pEngine;

	CCar() :m_pEngine(new CEngine2L) {}
	CCar(const string& type) :m_pEngine(type == "2.0L" ? new CEngine2L : new CEngine2L) {}

	void drive() {
		if (m_pEngine) {
			m_pEngine->working();
			cout << "汽车行驶中" << endl;
		}
	}
	~CCar() {
		if (m_pEngine)
			delete m_pEngine;
		m_pEngine = nullptr;
	}
};
int main()
{
	CCar bmw("2.0L");
	bmw.drive();//2.0L发动机正在工作 汽车行驶中
}

我们观察上述代码,如果我们想将CEngine2L修改成有参构造,这是不仅要对CEngine2L类进行修改,后续所有用到CEngine2L类方法的地方我们都要进行相应的修改,这种不但造成了类之间的耦合,还违背了开闭设计原则,为了解决这一问题,我们引入工厂模式。

4.1简单工厂

#include<iostream>
using namespace std;

class CEngine {
public:
	virtual void working() = 0;
};

class CEngine2L :public CEngine {
public:
	void working() {
		cout << "2.0L发动机正在工作" << endl;
	}
};

class CEngine2T :public CEngine {
public:
	void working() {
		cout << "2.0T发动机正在工作" << endl;
	}
};
//简单工厂
class CFactoryEngine {
public:
	CEngine* CreateEngine(const string& type) {
		if (type == "2.0L") {
			return new CEngine2L;
		}
		else if (type == "2.0T") {
			return new CEngine2T;
		}
		else {
			return nullptr;
		}
	}
};

class CCar {
public:
	CEngine* m_pEngine;

	//CCar() :m_pEngine(new CEngine2L) {}
	//CCar(const string& type) :m_pEngine(type == "2.0L" ? new CEngine2L : new CEngine2L) {}

	//引入简单工厂后的写法
	CCar(CFactoryEngine* pFac, const string& type) :m_pEngine(pFac ? pFac->CreateEngine(type) : nullptr) {};

	void drive() {
		if (m_pEngine) {
			m_pEngine->working();
			cout << "汽车行驶中" << endl;
		}
	}
	~CCar() {
		if (m_pEngine)
			delete m_pEngine;
		m_pEngine = nullptr;
	}
};
int main()
{
	CFactoryEngine Fac;

	CCar bmw(&Fac, "2.0L");
	bmw.drive();//2.0L发动机正在工作 汽车行驶中

	CCar benchi(&Fac, "2.0T");
	benchi.drive();//2.0T发动机正在工作 汽车行驶中
}

 

上例中createEngine方法包含了所有类型发动机(CEngine2L,CEngine2T)的创建,当增加新的产品种类时,除了要增加对应的类外,还要修改工厂的集中创建方法,违背了开闭原则,所以简单工厂适合创建种类比较少比较固定的对象,对于复杂的业务环境就不太适应了。

4.2工厂方法

由于简单工厂存在着弊端(违背开闭原则),所以在其基础上,进一步将工厂类进行抽象,拆分多个类型的工厂,每个工厂创建对应类型的类对象。

#include<iostream>
using namespace std;

class CEngine {
public:
	virtual void working() = 0;
};

class CEngine2L :public CEngine {
public:
	void working() {
		cout << "2.0L发动机正在工作" << endl;
	}
};

class CEngine2T :public CEngine {
public:
	void working() {
		cout << "2.0T发动机正在工作" << endl;
	}
};
//工厂方法
class CFactoryEngine {
public:
	virtual CEngine* CreateEngine() = 0;
};

class CFactoryEngine2L :public CFactoryEngine {
public:
	virtual CEngine* CreateEngine() {
		return new CEngine2L;
	}
};

class CFactoryEngine2T :public CFactoryEngine {
public:
	virtual CEngine* CreateEngine() {
		return new CEngine2T;
	}
};

class CCar {
public:
	CEngine* m_pEngine;

	//CCar() :m_pEngine(new CEngine2L) {}
	//CCar(const string& type) :m_pEngine(type == "2.0L" ? new CEngine2L : new CEngine2L) {}

	//引入简单工厂后的写法
	//CCar(CFactoryEngine* pFac, const string& type) :m_pEngine(pFac ? pFac->CreateEngine(type) : nullptr) {};

	//引入工厂方法后的写法
	CCar(CFactoryEngine* pFac) :m_pEngine(pFac ? pFac->CreateEngine() : nullptr) {};

	void drive() {
		if (m_pEngine) {
			m_pEngine->working();
			cout << "汽车行驶中" << endl;
		}
	}
	~CCar() {
		if (m_pEngine)
			delete m_pEngine;
		m_pEngine = nullptr;
	}
};
int main()
{
	CFactoryEngine* pFac2L = new CFactoryEngine2L;
	CFactoryEngine* pFac2T = new CFactoryEngine2T;

	CCar bmw(pFac2L);
	bmw.drive();//2.0L发动机正在工作 汽车行驶中

	CCar benchi(pFac2T);
	benchi.drive();//2.0T发动机正在工作 汽车行驶中
}

工厂方法模式每个子类工厂对应一个具体类型的对象,比如CFactoryEngine2L对应CEngine2L对象,遵循了开闭原则,提高了扩展性(如果增加了新的类型的发动机那么原有的发动机工厂不用改动),但如果对象种类较多,那么每一个都要对应一个工厂,需要大量的工厂得不偿失。

举例:我们新增加一个变速箱类

#include<iostream>
using namespace std;

class CEngine {
public:
	virtual void working() = 0;
};

class CEngine2L :public CEngine {
public:
	void working() {
		cout << "2.0L发动机正在工作" << endl;
	}
};

class CEngine2T :public CEngine {
public:
	void working() {
		cout << "2.0T发动机正在工作" << endl;
	}
};

class CGearBox {
public:
	virtual void working() = 0;
};

class CGearBoxAuto :public CGearBox {
public:
	void working() {
		cout << "自动挡变速箱正在工作" << endl;
	}
};

class CGearBoxManual :public CGearBox {
public:
	void working() {
		cout << "手动挡变速箱正在工作" << endl;
	}
};

//工厂方法
class CFactoryEngine {
public:
	virtual CEngine* CreateEngine() = 0;
};

class CFactoryEngine2L :public CFactoryEngine {
public:
	virtual CEngine* CreateEngine() {
		return new CEngine2L;
	}
};

class CFactoryEngine2T :public CFactoryEngine {
public:
	virtual CEngine* CreateEngine() {
		return new CEngine2T;
	}
};

class CFactoryGearBox {
public:
	virtual CGearBox* CreateGearBox() = 0;
};

class CFactoryGearBoxAuto :public CFactoryGearBox {
public:
	virtual CGearBox* CreateGearBox() {
		return new CGearBoxAuto;
	}
};

class CFactoryGearBoxManual :public CFactoryGearBox {
public:
	virtual CGearBox* CreateGearBox() {
		return new CGearBoxManual;
	}
};

class CCar {
public:
	CEngine* m_pEngine;
	CGearBox* m_pGearBox;
	//CCar() :m_pEngine(new CEngine2L) {}
	//CCar(const string& type) :m_pEngine(type == "2.0L" ? new CEngine2L : new CEngine2L) {}

	//引入简单工厂后的写法
	//CCar(CFactoryEngine* pFac, const string& type) :m_pEngine(pFac ? pFac->CreateEngine(type) : nullptr) {};

	//引入工厂方法后的写法
	CCar(CFactoryEngine* pFacEngine, CFactoryGearBox* pFacGearBox) :
		m_pEngine(pFacEngine ? pFacEngine->CreateEngine() : nullptr),
		m_pGearBox(pFacGearBox ? pFacGearBox->CreateGearBox() : nullptr) {};

	void drive() {
		if (m_pEngine && m_pGearBox) {
			m_pEngine->working();
			m_pGearBox->working();
			cout << "汽车行驶中" << endl;
		}
	}
	~CCar() {
		if (m_pEngine)
			delete m_pEngine;
		m_pEngine = nullptr;

		if (m_pGearBox)
			delete m_pGearBox;
		m_pGearBox = nullptr;
	}
};
int main()
{
	CFactoryEngine* pFac2L = new CFactoryEngine2L;
	CFactoryGearBox* pFacAuto = new CFactoryGearBoxAuto;

	CCar bmw(pFac2L, pFacAuto);
	bmw.drive(); //2.0L发动机正在工作 自动挡变速箱正在工作 汽车行驶中
}

4.3抽象工厂

#include<iostream>
using namespace std;

class CEngine {
public:
	virtual void working() = 0;
};

class CEngine2L :public CEngine {
public:
	void working() {
		cout << "2.0L发动机正在工作" << endl;
	}
};

class CEngine2T :public CEngine {
public:
	void working() {
		cout << "2.0T发动机正在工作" << endl;
	}
};

class CGearBox {
public:
	virtual void working() = 0;
};

class CGearBoxAuto :public CGearBox {
public:
	void working() {
		cout << "自动挡变速箱正在工作" << endl;
	}
};

class CGearBoxManual :public CGearBox {
public:
	void working() {
		cout << "手动挡变速箱正在工作" << endl;
	}
};

//工厂方法
//class CFactoryEngine {
//public:
//	virtual CEngine* CreateEngine() = 0;
//};
//
//class CFactoryEngine2L :public CFactoryEngine {
//public:
//	virtual CEngine* CreateEngine() {
//		return new CEngine2L;
//	}
//};
//
//class CFactoryEngine2T :public CFactoryEngine {
//public:
//	virtual CEngine* CreateEngine() {
//		return new CEngine2T;
//	}
//};
//
//class CFactoryGearBox {
//public:
//	virtual CGearBox* CreateGearBox() = 0;
//};
//
//class CFactoryGearBoxAuto :public CFactoryGearBox {
//public:
//	virtual CGearBox* CreateGearBox() {
//		return new CGearBoxAuto;
//	}
//};
//
//class CFactoryGearBoxManual :public CFactoryGearBox {
//public:
//	virtual CGearBox* CreateGearBox() {
//		return new CGearBoxManual;
//	}
//};

//抽象工厂
class CFactory {
public:
	virtual CEngine* CreateEngine() = 0;
	virtual CGearBox* CreateGearBox() = 0;
};

class CFac2LAuto :public CFactory {
public:
	virtual CEngine* CreateEngine() {
		return new CEngine2L;
	}
	virtual CGearBox* CreateGearBox() {
		return new CGearBoxAuto;
	}
};

class CCar {
public:
	CEngine* m_pEngine;
	CGearBox* m_pGearBox;
	//CCar() :m_pEngine(new CEngine2L) {}
	//CCar(const string& type) :m_pEngine(type == "2.0L" ? new CEngine2L : new CEngine2L) {}

	//引入简单工厂后的写法
	//CCar(CFactoryEngine* pFac, const string& type) :m_pEngine(pFac ? pFac->CreateEngine(type) : nullptr) {};

	//引入工厂方法后的写法
	/*CCar(CFactoryEngine* pFacEngine, CFactoryGearBox* pFacGearBox) :
		m_pEngine(pFacEngine ? pFacEngine->CreateEngine() : nullptr),
		m_pGearBox(pFacGearBox ? pFacGearBox->CreateGearBox() : nullptr) {};*/

	//引入抽象工厂后的写法
	CCar(CFactory* pFac):
		m_pEngine(pFac ? pFac->CreateEngine() : nullptr),
		m_pGearBox(pFac? pFac->CreateGearBox() : nullptr) {};

	void drive() {
		if (m_pEngine && m_pGearBox) {
			m_pEngine->working();
			m_pGearBox->working();
			cout << "汽车行驶中" << endl;
		}
	}
	~CCar() {
		if (m_pEngine)
			delete m_pEngine;
		m_pEngine = nullptr;

		if (m_pGearBox)
			delete m_pGearBox;
		m_pGearBox = nullptr;
	}
};
int main()
{
	CFactory* pFac = new CFac2LAuto;

	CCar bmw(pFac);
	bmw.drive(); //2.0L发动机正在工作 自动挡变速箱正在工作 汽车行驶中
}

对于具体工厂类可以根据需求*组合产品,如需求增加 CEngine2T和 CGearboxAuto 的组合,则新建工厂类CFactory2TAuto即可。

三种工厂方式总结:
1、对于简单工厂和工厂方法来说,两者的使用方式实际上是一样的,如果对于产品的分类和名称是确定的,数量是相对固定的,推荐使用简单工厂模式。
2、抽象工厂用来解决相对复杂的问题,适用于一系列、大批量的对象生产。

 如有错误,欢迎交流指正!

上一篇:从基础到进阶:Docker 实践与应用的全方位解析


下一篇:vgg19提取特征