C++设计模式
- 单例模式
1.1概念:一种创建型的设计模式。
1.2作用:保证一个类只有一个实例化对象,并提供一个访问它的全局访问点,使得系统中只有唯一的一个对象实例。
1.3应用: (1)需要频繁实例化然后销毁的对象。
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 有状态的工具类对象。
- 频繁访问数据库或文件的对象。
如日志文件、应用配置、线程池
1.4三个关键点:
- 单例类只能有一个实例化对象。
为此,单例类只能提供私有的构造函数,即保证不能随意创建该类的实例。
2、单例类必须自己提供一个实例化对象。
因为构造函数是私有的,其他对象不能创建单例类的实例,只能是单例类自己来创建。
3、单例类必须提供一个可以访问唯一实例化对象的接口。
外界需要获取并使用这个单例类的实例,但是由于该类的构造函数是私有的,外界无法通过new去获取它的实例,那么就必须提供一个静态的公有方法,该方法创建或者获取它本身的静态私有对象并返回。
1.5两种实现方式:
懒汉式:故名思义,懒汉很懒只有饿了才会去找吃的。也就是,只有在需要使用的时候才会去实例化。
饿汉式:饿了肯定要饥不择食。在单例类定义的时候就进行实例化。
1.5.1非线程安全的懒汉式单例模式:
class Singleton
{
public:
//(4)提供一个公有的静态成员函数用于返回实例,如果实例为NULL,则进行实例化。
static Singleton* getInstance();
private:
//(1)默认构造函数是私有的,外部不能进行单例类的实例化;
Singleton(){}
//(2)拷贝构造函数和赋值运算符也是私有的,以禁止拷贝和赋值;
Singleton(const Singleton&){}
Singleton& operator=(const Singleton&){}
//(3)具有一个私有的静态成员指针 instance_,指向唯一的实例;
static Singleton* instance_;
};
Singleton* Singleton::instance_ = NULL;
Singleton* Singleton::getInstance()
{
if (NULL == instance_)
instance_ = new Singleton();
return instance_;
}
1.5.2线程安全的懒汉式单例模式:
如果有两个线程同时获取单例类的实例,都发现实例不存在,因此都会进行实例化,就会产生两个实例都要赋值给instance_,这是严重的错误。为了解决这个问题,就要考虑加锁。
绝大多数情况下,获取实例时都是直接返回实例,这时候不会涉及到上锁、解锁的问题。只有在没有实例的时候,才会涉及上锁、解锁,这种情况是很少的。这个获取实例的方法对性能几乎无影响。
std::mutex mt;
class Singleton
{
public:
static Singleton* getInstance();
private:
Singleton(){}
Singleton(const Singleton&){}
Singleton& operator=(const Singleton&){}
static Singleton* instance_;
};
Singleton* Singleton::instance_ = NULL;
Singleton* Singleton::getInstance()
{
mt.lock(); //上锁
if (NULL == instance_)
{
instance_ = new Singleton();
}
return instance_;
mt.unlock(); //解锁
}
1.5.3 饿汉式单例模式:
饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化(不存在上述的线程安全问题)。在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
class Singleton
{
public:
static Singleton* getInstance();
private:
Singleton(){}
Singleton(const Singleton&){}
Singleton& operator=(const Singleton&){}
static Singleton* instance_;
};
Singleton* Singleton::m_pSingleton = new Singleton();
Singleton* Singleton::getInstance()
{
return m_pSingleton;
}
1.6 对象释放问题:
上边的程序中只有new,却没有delete,也就是说只有内存申请而没有内存释放,会不会有内存泄漏?
答:一般情况下,单例类的实例都是常驻内存的,一直存在于进程的生命周期,因此不需要手动释放。如果的确需要释放实例占用的内存,一定不能在单例类的析构函数中进行delete操作,这样会造成无限循环,可以考虑增加一个destroy方法用于释放内存,或者在单例类中定义一个内嵌的垃圾回收类。
(1)提供一个destroy接口,由这个单例的使用者来释放:
class Singleton
{
public:
...
void destory() //destroy接口
{
if (instance_!= NULL)
{
delete instance_;
instance_= NULL;
}
}
...
private:
...
static Singleton* instance_;
};
PS:一个容易犯的错误:
// 用户1
Singleton* mySinglePtr = Singleton::getInstance();
delete mySinglePtr;
mySinglePtr = NULL;
// 用户2在用户1delete后,使用instance_
Singleton* mySinglePtr2 = Singleton::getInstance();
mySinglePtr2->getVal();
// ****此时,出现segment default!!!
原因:用户1 delete mySinglePtr后,将Singleton的实例已经释放掉,但是但是!!Singleton中的instance_没有复位为空指针!!!!仍然指向之前new出来的内存(但这块内存已经被delete掉,系统已经回收)。用户2要使用instance_,Singleton::getInstance()判断此时的instance_不为空,就直接返回了instance_指针,instance_指向了已经被系统回收的内存,造成segment default。
- 在单例类中定义一个内嵌的垃圾回收类(自动回收);
class Singleton
{
public:
static Singleton* getInstance();
// 实现一个内嵌垃圾回收类
class CGarbo
{
public:
~CGarbo()
{
if(Singleton::m_instance_)
delete Singleton::m_instance_;
}
};
// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
static CGarbo Garbo;
private:
Singleton(){}
Singleton(const Singleton&){}
Singleton& operator=(const Singleton&){}
static Singleton* instance_;
};
2、工厂模式
2.1 作用:封装对象的创建,分离对象的创建和操作过程,用于批量管理对象的创建过程,便于程序的维护和扩展。工厂模式作为一种创建模式,一般在创建复杂对象时,考虑使用;在创建简单对象时,建议直接new完成一个实例对象的创建。
2.2 分类:简单工厂模式、工厂方法模式、抽象工厂模式。
2.2.1 简单工厂模式:
主要特点是需要在工厂类中做判断,从而创造相应的产品,当增加新产品时,需要修改工厂类。使用简单工厂模式,我们只需要知道具体的产品型号就可以创建一个产品。
缺点:工厂类集中了所有产品类的创建逻辑,如果产品量较大,会使得工厂类变的非常臃肿。
//定义产品类型信息
typedef enum ProductTypeTag
{
TypeA,
TypeB,
TypeC
}PRODUCTTYPE;
//产品抽象基类
class Product
{
public:
virtual void Show() = 0;
};
//具体的产品类
class ProductA : public Product
{
public:
void Show() { cout<<"I'm ProductA"<<endl; }
};
class ProductB : public Product
{
public:
void Show() { cout<<"I'm ProductB"<<endl; }
};
class ProductC : public Product
{
public:
void Show() { cout<<"I'm ProductC"<<endl; }
};
//工厂类
class Factory
{
public:
Product* CreateProduct(PRODUCTTYPE type)
{
switch (type)
{
case TypeA:
return new ProductA();
case TypeB:
return new ProductB();
case TypeC:
return new ProductC();
default:
return NULL;
}
}
};
//主函数
int main()
{
Factory productCreator;
Product *productA=productCreator.CreateProduct(TypeA);
Product *productB=productCreator.CreateProduct(TypeB);
Product *productC=productCreator.CreateProduct(TypeC);
productA->Show();
productB->Show();
productC->Show();
if(productA){
delete productA;
productA=NULL;
}
if(productB){
delete productB;
productB=NULL;
}
if(productC){
delete productC;
productC=NULL;
}
return 0;
}
2.2.2 工厂方法模式:
工厂方法模式在简单工厂模式的基础上增加对工厂的基类抽象,不同的产品创建采用不同的工厂创建(从工厂的抽象基类派生),这样创建不同的产品过程就由不同的工厂分工解决:FactoryA专心负责生产ProductA,FactoryB专心负责生产ProductB,FactoryA和FactoryB之间没有关系;如果到了后期,如果需要生产ProductC时,我们则可以创建一个FactoryC工厂类,该类专心负责生产ProductC类产品。
该模式相对于简单工厂模式的优势在于:便于后期产品种类的扩展。
缺点:产品类数据较多时,需要实现大量的工厂类,这无疑增加了代码量。
//抽象工厂类,提供一个创建接口
class Factory
{
public:
//提供创建产品实例的接口,返回抽象产品类
virtual Product *createProduct()=0;
};
//具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
class FactoryA : public Factory
{
public:
Product *createProduct() { return new ProductA(); }
};
class FactoryB : public Factory
{
public:
Product *createProduct() { return new ProductB(); }
};
class FactoryC : public Factory
{
public:
Product *createProduct() { return new ProductC(); }
};
//主函数
int main()
{
Factory *factoryA=new FactoryA();
Product *productA = factoryA->createProduct();
productA->Show();
Factory *factoryB=new FactoryB();
Product *productB = factoryB->createProduct();
productB->Show();
if (factoryA){
delete factoryA;
factoryA = NULL;
}
if (factoryB){
delete factoryB;
factoryB = NULL;
}
if (productA){
delete productA;
productA = NULL;
}
if (productB){
delete productB;
productB = NULL;
}
return 0;
}
2.2.3 抽象工厂模式:
抽象工厂模式提供创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。当存在多个产品系列,而客户端只使用一个系列的产品时,可以考虑使用抽象工厂模式。
缺点:当增加一个新系列的产品时,不仅需要现实具体的产品类,还需要增加一个新的创建接口,扩展相对困难。
/* 关键代码:在一个工厂里聚合多个同类产品。
* 以下代码以低端产品和高端产品为例,低端产品为一个产品系列,高端产品为一个产品系列。ProductA1和ProductB1为低端产品,ProductA2和ProductB2为高端产品。每个系列的衣服由一个对应的工厂创建,这样一个工厂创建的产品能保证产品为同一个系列。
*/
class ProductA
{
public:
virtual void Show() = 0;
};
//A类低端产品
class ProductA1 : public ProductA
{
public:
void Show() { cout<<"I'm ProductA1"<<endl; }
};
//A类高端产品
class ProductA2 : public ProductA
{
public:
void Show() { cout<<"I'm ProductA2"<<endl; }
};
class ProductB
{
public:
virtual void Show() = 0;
};
//B类低端产品
class ProductB1 : public ProductB
{
public:
void Show() { cout<<"I'm ProductB1"<<endl; }
};
//B类高端产品
class ProductB2 : public ProductB
{
public:
void Show() { cout<<"I'm ProductB2"<<endl; }
};
class Factory
{
public:
virtual ProductA *CreateProductA() = 0;
virtual ProductB *CreateProductB() = 0;
};
//1号工厂用于生产低端产品
class Factory1 : public Factory
{
public:
ProductA *CreateProductA() { return new ProductA1(); }
ProductB *CreateProductB() { return new ProductB1(); }
};
//2号工厂用于生产高端产品
class Factory2 : public Factory
{
public:
ProductA *CreateProductA() { return new ProductA2(); }
ProductB *CreateProductB() { return new ProductB2(); }
};
int main()
{
Factory *factory1 = new Factory1();
ProductA *productA1 = factory1->CreateProductA();
ProductB *productB1 = factory1->CreateProductB();
productA1->Show();
productB1->Show();
Factory *factory2 = new Factory2();
ProductA *productA2 = factory2->CreateProductA();
ProductB *productB2 = factory2->CreateProductB();
productA2->Show();
productB2->Show();
if (factory1){
delete factory1;
factory1 = NULL;
}
if (productA1){
delete productA1;
productA1= NULL;
}
if (productB1){
delete productB1;
productB1 = NULL;
}
if (factory2){
delete factory2;
factory2 = NULL;
}
if (productA2){
delete productA2;
productA2 = NULL;
}
if (productB2){
delete productB2;
productB2 = NULL;
}
}