一、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导致崩溃
解决方法:
- 禁止复制类
- 显示定义复制语义
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. 单例的实现
- 如果不让用户创建新的实例,声明私有默认构造函数
- 确保只有一个实例(不可复制),声明私有复制构造函数和私有复制赋值操作符
- 如果禁止用户删除单例,声明私有析构函数
- 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. 抽象基类
#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. 扩展工厂示例
- 将具体的派生类和工厂方法解耦
- 支持在运行时添加新的派生类
- 工厂类维护一个映射,此映射将类型名与创建对象的回调关联起来
- 允许新的派生类通过一对新的方法调用来实现注册和注销
注意:工厂对象必须保存其状态信息,因此最好强制要求任意时刻都之只能创建一个工厂对象(常常使用单例),为了简洁,此处使用静态方法和变量
#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 包装器模式
包装器模式使用场景:
- 当维护一个大型遗留代码,相比于重构所有代码,更好的方法时设计一个新的、更简介的API,以隐藏所有底层遗留代码
- 已经编写了一个C++API,现在需要给特定用户提供纯C接口
- 写的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是第三方类库。
使用代理模式的案例:
- 实现原始对象的惰性实例:知道方法被调用时,Original对象才真正实例化
- 实现对Original对象的访问控制:例如可以在Proxy和Original对象之间插入权限层,当用户获得权限后才能调用Original对象上的特定方法
- 保证Original类线程安全:例如可以在Proxy和Original对象之间添加互斥锁
- 支持资源共享:让多个Proxy对象共享相同的Original基础类(减小内存占用),例如可以用于实现引用计数或写时复制语义
- 应对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;
}
适配器模式的优点:
- 强制API始终保持一致性:如果有多个类且这些类的接口风格不同,使用适配器可以整合这些类,进而对外提供一致的接口
- 包装API的依赖库
- 转换数据类型
- 为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;
}
外观模式的用途:
- 隐藏遗留代码
- 创建便捷API
- 支持简化功能或者替代功能的API
五、观察者模式
观察者支持组件解耦,且避免了循环依赖
1. MVC(Model-View-Controller)架构
MVC架构模式要求业务逻辑(Model 模型)独立于用户界面(View 视图),控制器(Controller)接收用户输入并协调另外两者(控制器逻辑引起模型的改变并更新视图),MVC支持应用程序模块化
MVC优点:
- 模型和视图组件隔离:可以实现多个界面,多个界面能够重用公共的业务逻辑
- 避免因多份UI实现而构建多份重复的底层代码逻辑
- 模型和视图代码的解耦简化了为核心业务逻辑代码编写单元测试的工作
- 组件模块化可以使业务逻辑和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;
}