C++ 常用设计模式和惯用法

一、Pimpl (pointer to implementation 指向实现的指针)惯用法

知识点:

使用Pimpl惯用法将实现细节从公有头文件中分离出来

1. 使用Pimpl

将类的数据成员定义为指向某个已经声明过的类型的指针

//自动定时器

//不好的设计、包含与平台相关的定义,暴露了定时器在不同平台上的底层实现细节
#ifdef _WIN32
#include <windows.h>
#else 
#include <sys/time.h>
#endif

#include <string>

class AutoTimer
{
public:
//构造定时器
explicit AutoTimer(const std::string& name)
{
    //...
}
//销毁定时器
~AutoTimer()
{
    //...
}

private:
//返回定时器存在了多长时间
double GetElapsed() const
{
    //...
}
std::string m_Name;

#ifdef _WIN32
DWORD m_StartTime;
#else
struct timeval m_StartTime
#endif// !_WIN32

};
/**
* 使用Pimpl惯用法
* 将所有私有成员放到一个类(或结构体中),这个类在头文件中前置声明,在.cpp中定义
*/
#include <string>

class AutoTimer02
{
public:
	explicit AutoTimer02(const std::string& name);
	~AutoTimer02();

private:
	//内嵌类声明为私有
	class Pimpl;
	Pimpl* m_Pimpl;
};

//AutoTimer02.cpp

#include "AutoTimer02.h"
#include <iostream>
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/time.h>
#endif

class AutoTimer02::Pimpl
{
public:
	double GetElapsed() const
	{
#ifdef _WIN32
		return (GetTickCount() - m_StartTime) / 1e3;
#else 
		struct timeval end_time;
		gettimeofday(&end_time, NULL);
		double t1 = m_StartTime.tv_usec / 1e6 + m_StartTime.tv_sec;
		double t2 = end_time.tv_usec / 1e6 + end_time.tv_sec;
		return t2 - t1;
#endif // _WIN32

	}

	std::string m_Name;

#ifdef _WIN32
	DWORD m_StartTime;
#else
	struct timeval m_StartTime
#endif// !_WIN32
};

AutoTimer02::AutoTimer02(const std::string& name)
	:m_Pimpl(new Pimpl())
{
	m_Pimpl->m_Name = name;
#ifdef _WIN32
	m_Pimpl->m_StartTime = GetTickCount();
#else
	gettimeofday(&m_Pimpl->m_StartTime, NULL);
#endif// !_WIN32
}

AutoTimer02::~AutoTimer02()
{
	delete m_Pimpl;
	m_Pimpl = nullptr;
}

2. 复制语义

如果没有为类显示定义复制构造函数和赋值操作符,C++编译器会默认创建(浅复制),如果用户复制对象,则这两个对象指向同一个Impl实现对象,析构的时候会删除同一个Impl导致崩溃

解决方法:

  1. 禁止复制类
  2. 显示定义复制语义

3. Pimpl与智能指针

使用智能指针管理指向Pimpl对象的指针的初始化和销毁,不需要在析构函数中显示释放指针

//使用智能指针
#include <string>
#include <memory>

class AutoTimer03
{
public:
	explicit AutoTimer03(const std::string& name);
	~AutoTimer03();

private:
	//内嵌类声明为私有
	class Pimpl;
	std::shared_ptr<Pimpl> m_Pimpl;
};


4. Pimpl的优点

  • 信息隐藏:私有成员可以完全隐藏在公有接口之外
  • 降低耦合:使用Pimpl可以将依赖转移到.cpp中
  • 加速编译
  • 更好的二进制兼容性:采用Pimpl的对象大小不会改变(一个指向实现的指针)
  • 惰性分配:Pimpl类可以在需要时再进行构造

5. Pimpl的缺点(开发人员必须承担的负担,利大于弊)

  • 必须为创建的每个对象分配并释放实现对象。
  • 给开发人员带来不便,所有访问的私有成员都必须通过 Pimp-> 访问
  • 编译器将不在能够捕获const方法中对成员变量的修改,因为成员变量现在存在于独立的变量中,编译器仅检查const方法中Pimpl指针的值是否发生变化,而不是Pimpl指向的成员
//可以修改m_Impl指针指向的值,但是m_Impl本身不可修改
void PimpledObject::ConstMethod() const
{
    m_Impl->m_name = "haha";			
}

二、单例

单例设计模式用来确保一个类仅存在一个实例,该模式亦提供对此唯一实例的全局访问点。

知识点:

单例是一种更加优雅的维护全局状态的方式,但始终应该考虑清楚是否需要全局状态

1. 单例的实现

  1. 如果不让用户创建新的实例,声明私有默认构造函数
  2. 确保只有一个实例(不可复制),声明私有复制构造函数和私有复制赋值操作符
  3. 如果禁止用户删除单例,声明私有析构函数
  4. Getinstance() 既可以返回单例的指针,也可以返回引用,当返回指针时用户可以删除该对象,因此最好返回引用

//不是线程安全的单例
class SingleTon
{
public:
	//注意:不同编译单元中非局部静态变量初始化顺序是未定义的,也就是说
	// 使用非局部静态变量初始化单例是非常危险的
	//在类的方法中创建局部静态变量
	static SingleTon& GetInstance()
	{
		static SingleTon instance;
		return instance;
	}

private:
	//注意:声明私有构造函数和析构函数意味着用户不能创建单例的子类,
	//如果要这么做,可以将这两个函数声明为受保护的
	SingleTon();
	~SingleTon();
	
	SingleTon(const SingleTon& o);
	const SingleTon& operator= (const SingleTon& o);
};

//使用Singleton
//SingleTon& obj = SingleTon::GetInstance();

2. 使单例线程安全

上面这个单例不是线程安全的,因为在单例的静态初始化中存在竞争条件,如果碰巧有两个线程同时调用该方法,那么实例就可能被构造两次,或者在一个线程完全初始化之前另一个线程就调用了该实例

//编译器可能生成的实例代码
Singleton& Singleton::GetInstance()
{
    extern void __DestructSingleton();
    static char __buffer[sizeof(Singleton)];
    static bool __initialized = false;
    if(! ____initialized)
    {
        new (__buffer)Singleton;			//placement new语法
        atexit(__DestructSingleton);		//退出时销毁实例
        __initialized = true;
    }
    return *reinterpret_Cast<Singleton* >(__buffer);
}

void __DestructSingleton()
{
    //调用静态__buffer单例对象的析构函数
}

(1)解决线程安全的基本思路就是加锁(这种方式的缺点时,加锁的开销)

static Mutex mutex;
static SingleTon& GetInstance()
{
    ScopeLock lock(&mutex);			//在函数退出时释放互斥锁
    static SingleTon instance;
    return instance;
}

(2)静态初始化:在main函数之前初始化(通常可以认为程序这时是单线程的)

static Singleton& foo = Singleton::GetInstance(); 

(3)显示API初始化:将单例初始化作为库初始化例程的一部分

static Mutex mutex;
void APIInitialiaze()
{
    ScopeLock lock(&mutex);	
    SingleTon::GetInstance();
}

3. 单例与依赖注入

class MyClass
{
	MyClass():
    	m_Database(new Database("mydb","localhost","user","pass"))
	{
        
    }
private:
	Database* m_database;
}

//使用依赖注入
class MyClass
{
	MyClass(Database* db):
    	m_Database(db)
	{
        
    }
private:
	Database* m_database;
}

4. 单例与单一状态

单一状态模式与单例的主要区别是,单一状态可以创建多个实例,多个实例共享一份数据

class Monostate
{
public:
	int GetTheAnswer() const {return m_Answer;}
private:
static int m_Answer;
}

//int Monostate::m_Answer = 100;
//如果想从头文件中完全隐去声明,可以将该静态变量声明为monostate.cpp文件作用域的
//静态变量

三、工厂模式

工厂模式是一种创建型模式,它允许创建对象时不指定要创建对象的具体类型。本质上,工厂模式使构造函数的一般化。

构造函数的几个限制:

  1. 没有返回值
  2. 命名限制(和类名相同)
  3. 静态绑定创建,在构造函数构造时,构造类型必须时编译器已知的具体类型
  4. 不允许虚构造函数,必须指定编译时要构造的对象的精确类型,不能在构造函数这种调用虚方法(因为派生类此时还没有初始化)

1. 抽象基类

#pragma once
#include <string>
//抽象基类

class IRender
{
public:
	virtual ~IRender(){}			//抽象基类的析构函数必须为虚函数
	virtual bool LoasScene(const std::string&  fileName) = 0;
	virtual void SetViewportSize(int w,int h) = 0;
	virtual void SetCameraPositon(double x, double y, double z) = 0;
	virtual void SetLookAt(double x, double y, double z) = 0;
	virtual void Render() = 0;
};

2. 工厂示例

三个派生类头文件(他们不会出现在共有头文件RenderFactory.h中)他们都是私有头文件不需要和API一起发布,因此用户永远看不到渲染器的实现细节

#pragma once
#include "IRender.h"

class DirectxRenderer : public IRender
{
public:
DirectxRenderer() {}
~DirectxRenderer() {}

bool LoasScene(const std::string& fileName) override
{
	return true;
}
void SetViewportSize(int w, int h) override
{

}
void SetCameraPositon(double x, double y, double z) override
{

}
void SetLookAt(double x, double y, double z) override
{

}
void Render() override
{

}
};
#pragma once
#include "IRender.h"

class MesaRendreer : public IRender
{
public:
	MesaRendreer() {}
	~MesaRendreer() {}

	bool LoasScene(const std::string& fileName) override
	{
    	return true;
	}
	void SetViewportSize(int w, int h) override
	{

	}
	void SetCameraPositon(double x, double y, double z) override
	{

	}
	void SetLookAt(double x, double y, double z) override
	{

	}
	void Render() override
	{

	}
};
#pragma once
#include "IRender.h"

class OpenGlRenderer : public IRender
{
public:
	OpenGlRenderer() {}
	~OpenGlRenderer() {}

	bool LoasScene(const std::string& fileName) override
	{
    	return true;
	}
	void SetViewportSize(int w, int h) override
	{

	}
	void SetCameraPositon(double x, double y, double z) override
	{

	}
	void SetLookAt(double x, double y, double z) override
	{

	}
	void Render() override
	{

	}
};
#pragma once

#include "IRender.h"

class RenderFactory
{
public:
	IRender* CreateRenderer(const std::string& type);
};#pragma once

#include "IRender.h"

class RenderFactory
{
public:
	IRender* CreateRenderer(const std::string& type);
};

#include "RenderFactory.h"
#include "OpenglRendreer.h"
#include "DirectxRenderer.h"
#include "MesaRendreer.h"

//(运行时)根据用户传递的类型创建派生类
IRender* RenderFactory::CreateRenderer(const std::string& type)
{
	if (type == "opengl")
		return new OpenGlRenderer();
	if (type == "directx")
		return new DirectxRenderer();
	if (type == "mesa")
		return new MesaRendreer();
}

简单工厂的缺点:

  • 包含了可用派生类的硬编码信息,若要增加派生类必须修改RenderFactory.cpp文件

3. 扩展工厂示例

  1. 将具体的派生类和工厂方法解耦
  2. 支持在运行时添加新的派生类
  3. 工厂类维护一个映射,此映射将类型名与创建对象的回调关联起来
  4. 允许新的派生类通过一对新的方法调用来实现注册和注销

注意:工厂对象必须保存其状态信息,因此最好强制要求任意时刻都之只能创建一个工厂对象(常常使用单例),为了简洁,此处使用静态方法和变量

#pragma once
#include <string>
#include <map>
#include "IRender.h"

class RenderFactory02
{
public:
	//typedef IRender* (*CreateCallback)();
	using CreateCallback = IRender* (*)();
	static void RegisterRender(const std::string& type, CreateCallback cb);
	static void UnregisterRender(const std::string& type);
	static IRender* CreateRenderer(const std::string& type);

private:
	using CallbackMap = std::map<std::string, CreateCallback>;
	static CallbackMap m_Renders;
};

#include "RenderFactory02.h"

RenderFactory02::CallbackMap RenderFactory02::m_Renders = {};

void RenderFactory02::RegisterRender(const std::string& type, CreateCallback cb)
{
	m_Renders.emplace(std::make_pair(type,cb));
}

void RenderFactory02::UnregisterRender(const std::string& type)
{
	m_Renders.erase(type);
}

IRender* RenderFactory02::CreateRenderer(const std::string& type)
{
	auto itFind = m_Renders.find(type);
	if (itFind != m_Renders.end())
	{
		//调用回调以构造此派生类的对象
		return (itFind->second)();
	}
	return nullptr;
}
#include "IRender.h"
#include "RenderFactory02.h"

//新派生的类
class UserRender : public IRender
{
public:
	UserRender() {}
	~UserRender() {}

	bool LoasScene(const std::string& fileName) override
	{
		return true;
	}
	void SetViewportSize(int w, int h) override
	{

	}
	void SetCameraPositon(double x, double y, double z) override
	{

	}
	void SetLookAt(double x, double y, double z) override
	{

	}
	void Render() override
	{

	}

	//创建派生类对象的方法 用于注册到工厂类
	static IRender* CreateUserRender()
	{
		return new UserRender();
	}
};

int main(int argc, char* argv[])
{
	//注册一个新的渲染器
	RenderFactory02::RegisterRender("user", UserRender::CreateUserRender);

	//创建实例
	IRender* r = RenderFactory02::CreateRenderer("user");
	r->Render();
	delete r;
	r = nullptr;

	return 0;
}

四、API 包装器模式

包装器模式使用场景:

  1. 当维护一个大型遗留代码,相比于重构所有代码,更好的方法时设计一个新的、更简介的API,以隐藏所有底层遗留代码
  2. 已经编写了一个C++API,现在需要给特定用户提供纯C接口
  3. 写的API用到了一个第三方依赖库,但是不想将此库直接暴露给用户

1. 代理模式

代理设计模式为另一个类提供一对一的转发接口, 此模式通常实现是,代理类中存储原始类的副本或者指向原始类的指针,然后代理类中的方法将重定向到原始类对象中的同名方法。

缺点:需要再次暴露原始对象的函数,此过程本质上就是复制代码,因此在在改变原始对象时,需要同时维护代理接口

#pragma once
#include <iostream>

using namespace std;

class Original
{
public:
void DoSomethiing(int nValue);
};
#include "Original.h"

void Original::DoSomethiing(int nValue)
{
	cout << "call Original::DoSomethiing" << endl;
}
#pragma once
#include "Original.h"

class Proxy
{
public:
	Proxy();
	~Proxy();
	void DoSomething(int nValue);

private:
	Proxy(const Proxy& p);
	const Proxy& operator= (const Proxy& o);

	Original* m_pOriginal;
};
#include "Proxy.h"

Proxy::Proxy() : m_pOriginal(new Original())
{
	cout << "call Proxy::Proxy()" << endl;
}

Proxy::~Proxy()
{
	cout << "call Proxy::~Proxy()" << endl;
	delete m_pOriginal;
	m_pOriginal = nullptr;
}

void Proxy::DoSomething(int nValue)
{
	cout << "call Proxy::DoSomething" << endl;
	if(m_pOriginal)
		return m_pOriginal->DoSomethiing(nValue);
}
#include "Proxy.h"

int main(int, char**)
{
	Proxy proxy;
	proxy.DoSomething(42);

	return 0;
}

进阶版,在此基础上增加代理和原始API共享的虚接口(前提是能够修改原始API)

#pragma once

class IOriginal
{
public:
	virtual ~IOriginal() {};
	virtual void DoSomething(int nValue) = 0;
};
#pragma once
#include <iostream>
#include "IOriginal.h"

using namespace std;

class Original : public IOriginal
{
public:
	void DoSomething(int nValue) override;
};
#include "Original.h"

void Original::DoSomething(int nValue)
{
	cout << "call Original::DoSomething" << endl;
}
#pragma once
#include "Original.h"

class Proxy : public IOriginal
{
public:
	Proxy();
	~Proxy();
	void DoSomething(int nValue) override;

private:
	Proxy(const Proxy& p);
	const Proxy& operator= (const Proxy& o);

	Original* m_pOriginal;
};
#include "Proxy.h"

Proxy::Proxy() : m_pOriginal(new Original())
{
	cout << "call Proxy::Proxy()" << endl;
}

Proxy::~Proxy()
{
	cout << "call Proxy::~Proxy()" << endl;
	delete m_pOriginal;
	m_pOriginal = nullptr;
}

void Proxy::DoSomething(int nValue)
{
	cout << "call Proxy::DoSomething" << endl;
	if(m_pOriginal)
		return m_pOriginal->DoSomething(nValue);
}
#include "Proxy.h"

int main(int, char**)
{
	Proxy proxy;
	proxy.DoSomething(42);

	return 0;
}

如果要修改Original类的行为但要保持它的接口不变,那么代理模式非常合适,尤其当Original是第三方类库。

使用代理模式的案例:

  1. 实现原始对象的惰性实例:知道方法被调用时,Original对象才真正实例化
  2. 实现对Original对象的访问控制:例如可以在Proxy和Original对象之间插入权限层,当用户获得权限后才能调用Original对象上的特定方法
  3. 保证Original类线程安全:例如可以在Proxy和Original对象之间添加互斥锁
  4. 支持资源共享:让多个Proxy对象共享相同的Original基础类(减小内存占用),例如可以用于实现引用计数或写时复制语义
  5. 应对Original类将来被修改的情况:当基础库发生改变时,可以通过代理对象预留老接口

2. 适配器模式

将一个类的接口转换为一个兼容的但不相同的接口。与代理模式的相似之处是,适配器模式也是一个单一组件包装器,但适配器类和原始类的接口可以不同(和代理模式的主要区别)。

#pragma once
#include <iostream>

using namespace std;

class Original
{
public:
	void DoSomething(int nValue,bool bPrint);
};
#include "Original.h"

void Original::DoSomething(int nValue, bool bPrint)
{
	cout << "call Original::DoSomething" << endl;
	cout << "Original::DoSomething bPrint" << endl;
}
#pragma once
#include "Original.h"


class Adapter
{
public:
Adapter();
~Adapter();
void DoSomething(int nValue);

private:
Adapter(const Adapter& p);
const Adapter& operator= (const Adapter& o);

Original* m_pOriginal;
};
#include "Adapter.h"

Adapter::Adapter() : m_pOriginal(new Original())
{
	cout << "call Adapter::Adapter()" << endl;
}

Adapter::~Adapter()
{
	cout << "call Adapter::~Adapter()" << endl;
	delete m_pOriginal;
	m_pOriginal = nullptr;
}

void Adapter::DoSomething(int nValue)
{
    //和代理模式的主要区别 适配器模式接口可以不同
	cout << "call Adapter::DoSomething" << endl;
	if(m_pOriginal)
		return m_pOriginal->DoSomething(nValue,true);
}
#include "Adapter.h"

int main(int, char**)
{
	Adapter adapter;
	adapter.DoSomething(42);

	return 0;
}

适配器模式的优点:

  1. 强制API始终保持一致性:如果有多个类且这些类的接口风格不同,使用适配器可以整合这些类,进而对外提供一致的接口
  2. 包装API的依赖库
  3. 转换数据类型
  4. 为API暴露一个不同的调用约定

3. 外观模式

外观模式是多组件包装器的一个示例。外观模式和适配器模式的区别是外观模式简化了类的结构,而适配器模式仍然保持相同的类结构

外观模式为一组类提供了简化的接口,在封装外观模式中,底层类不再可访问

#pragma once

class Original1
{
public:
	void DoSometing(int nValue);
};

class Original2
{
public:
	void DoSometing();
};
#include "Original.h"
#include <iostream>
using namespace std;

void Original1::DoSometing(int nValue)
{
	cout << "Original1::DoSometing nValue" << nValue << endl;
}

void Original2::DoSometing()
{
	cout << "Original2::DoSometing" << endl;
}
#pragma once
#include "Original.h"

class Impl
{
public:
	Impl();
	~Impl();

	Original1* GetOriginal1();
	Original2* GetOriginal2();
private:
	Original1* m_original1;
	Original2* m_original2;
};

class Facade
{
public:
	Facade();
	~Facade();
 	void DoSometing();
private:
	Facade(const Facade& f);
	const Facade& operator= (const Facade& o);

	Impl* m_pImpl;
};
#include "Facade.h"
#include <iostream>

using namespace std;

/Impl
Impl::Impl()
	:m_original1(nullptr),m_original2(nullptr)
{

}

Impl::~Impl()
{
	if (m_original1)
	{
		delete m_original1;
		m_original1 = nullptr;
	}
	if (m_original2)
	{
		delete m_original2;
		m_original2 = nullptr;
	}
}

Original1* Impl::GetOriginal1()
{
	if (!m_original1)
	{
		cout << "create Original1" << endl;
		m_original1 = new Original1();
	}
	return m_original1;
}

Original2* Impl::GetOriginal2()
{
	if (!m_original2)
	{
		cout << "create Original2" << endl;
		m_original2 = new Original2();
	}
	return m_original2;
}

/Facade
Facade::Facade()
	:m_pImpl(new Impl())
{

}
Facade::~Facade()
{
	if (m_pImpl)
	{
		delete m_pImpl;
		m_pImpl = nullptr;
	}
}
void Facade::DoSometing()
{
	if (m_pImpl)
	{
		m_pImpl->GetOriginal1()->DoSometing(32);
		m_pImpl->GetOriginal2()->DoSometing();
	}
}
#include "Facade.h"

int main(int argc, char* argv[])
{
	Facade* pFacade = new Facade();
	pFacade->DoSometing();
	
	delete pFacade;
	pFacade = nullptr;
	return 0;
}

外观模式的用途:

  1. 隐藏遗留代码
  2. 创建便捷API
  3. 支持简化功能或者替代功能的API

五、观察者模式

观察者支持组件解耦,且避免了循环依赖

1. MVC(Model-View-Controller)架构

MVC架构模式要求业务逻辑(Model 模型)独立于用户界面(View 视图),控制器(Controller)接收用户输入并协调另外两者(控制器逻辑引起模型的改变并更新视图),MVC支持应用程序模块化

MVC优点:

  1. 模型和视图组件隔离:可以实现多个界面,多个界面能够重用公共的业务逻辑
  2. 避免因多份UI实现而构建多份重复的底层代码逻辑
  3. 模型和视图代码的解耦简化了为核心业务逻辑代码编写单元测试的工作
  4. 组件模块化可以使业务逻辑和GUI同时进行开发工作,互不干扰,提高开发效率

2. 实现观察者模式

实现观察者模式的典型做法是引入两个概念:主题(Subject)和观察者(Observer)。一个或多个观察者注册主题中感兴趣的事件,之后主题会在自身状态发生变化时通知所有注册的观察者

#pragma once

//观察者抽象类
class IObserver
{
public:
virtual ~IObserver() {}
virtual void Update(int message) = 0;
};
#pragma once
#include <vector>
#include <map>
#include "IObserver.h"

using namespace std;

using vecObserver = vector<IObserver*>;
using mapObserver = map<int, vecObserver>;

enum class MessageType
{
	MessageType_Add = 0,
	MessageType_Del,
	MessageType_Update,
};

//主题抽象类
class ISubject
{
public:
	ISubject() {}
	virtual ~ISubject() {}

	//注册订阅
	virtual void Subscribe(int message, IObserver* pObserver) = 0;
	//删除订阅
	virtual void UnSubscribe(int message, IObserver* pObserver) = 0;
	//通知
	virtual	void Notify(int message) = 0;

};
#pragma once
#include <string>
#include "IObserver.h"
using namespace std;

class MyObserver : public IObserver
{
public:
	MyObserver(const string& name);
	~MyObserver();
	
	void Update(int message)override;
private:
	string m_observerName;
};
#include "MyObserver.h"
#include <iostream>

using namespace std;

MyObserver::MyObserver(const string& name)
	: m_observerName(name)
{

}

MyObserver::~MyObserver()
{

}

void MyObserver::Update(int message)
{
	cout << "Observer name:" << m_observerName 
		<< "Received message:" << message  << endl;
}
#pragma once
#include "ISubject.h"

class MySubject : public ISubject
{
public:
	MySubject();
	~MySubject();
	void Subscribe(int message, IObserver* pObserver) override;
	void UnSubscribe(int message, IObserver* pObserver) override;
	void Notify(int message) override;
private:
	//所有注册的观察者
	mapObserver m_mapObserver;
};
#include "MySubject.h"

MySubject::MySubject()
{

}

MySubject::~MySubject()
{

}

void MySubject::Subscribe(int message, IObserver* pObserver)
{
	if (pObserver)
	{
		auto itFind = m_mapObserver.find(message);
		if (itFind == m_mapObserver.end())
		{
			m_mapObserver[message] = vecObserver();
		}
		m_mapObserver[message].emplace_back(pObserver);
	}
}

void MySubject::UnSubscribe(int message, IObserver* pObserver)
{
	auto itFind = m_mapObserver.find(message);
	if (itFind != m_mapObserver.end())
	{
		for (auto it = m_mapObserver[message].begin(); it != m_mapObserver[message].end();)
		{
			if ((*it) == pObserver)
			{
				it = m_mapObserver[message].erase(it);
			}
			else
			{
				++it;
			}
		}
		
	}
}

void MySubject::Notify(int message)
{
	auto itFind = m_mapObserver.find(message);
	if (itFind != m_mapObserver.end())
	{
		for (const auto& it : itFind->second)
		{
			it->Update(message);
		}
	}
}
#include "MySubject.h"
#include "MyObserver.h"

int main(int argc, char* argvp[])
{
	//主题
	MySubject mySubject;

	//创建三个观察者
	MyObserver myObserver1("myObserver1");
	MyObserver myObserver2("myObserver2");
	MyObserver myObserver3("myObserver3");

	//订阅
	mySubject.Subscribe((int)MessageType::MessageType_Add,&myObserver1);
	mySubject.Subscribe((int)MessageType::MessageType_Del,&myObserver1);
	mySubject.Subscribe((int)MessageType::MessageType_Del,&myObserver2);
	mySubject.Subscribe((int)MessageType::MessageType_Update,&myObserver3);

	//通知
	mySubject.Notify((int)MessageType::MessageType_Add);
	mySubject.Notify((int)MessageType::MessageType_Del);
	mySubject.Notify((int)MessageType::MessageType_Update);

	//解除订阅
	mySubject.UnSubscribe((int)MessageType::MessageType_Del, &myObserver1);

	//通知
	mySubject.Notify((int)MessageType::MessageType_Add);
	mySubject.Notify((int)MessageType::MessageType_Del);
	mySubject.Notify((int)MessageType::MessageType_Update);
	return 0;
}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
作者:(美)默里 著,王昕 译 出版日期:2004-2-1 出版社:其它 页数:208 ISBN:9787508319124 文件格式:PDF 书籍简介 本书第1版荣获美国“软件开发”杂志评选的1996年图书震撼大奖(Jolt Award),中文版自2000年推出以来,经久不衰,获得了读者的充分肯定和高度评价。 第2版与第1版相比,在章节安排上有以下改变。增加了两章:“对象的创建与使用”和“C++中的C”。前者与“对象导言”实际上是第1版“对象的演化”一 章的彻底重写,增加了近几年面向对象方和编程方的最新研究与实践的丰硕成果;后者的添加使不熟悉C的读者可以直接使用本书。删去了四章:“输入输出流 介绍”、“多重继承”、“异常处理”和“运行时类型识别”,删去的内容均为C++中较复杂的主题,作者将它们连同C++标准完成后增加的一些内容放到本书 的第2卷中,使本书的第1卷内容显得更加集中,可以供不同程度的读者选择阅读。需要强调的是,第2版的改变不仅体现在这些章节的调整上,更多的改变体现在 每一章的字里行间,包括例子的调整和练习的补充。与众不同的精心选材和认真推敲的叙述使得第2版更趋成熟。 本书是C++领域内一本权威的著作,书中的内容、讲授方、例子和练习既适合课堂教学,又适合读者自学。无论是高等院校计算机及相关专业的学生,还是业界的从业人员,以及广大的计算机爱好者,都可从阅读本书中获益。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值