势者,因利而制权也。——《孙子兵法》
软件的设计模式
- 一个默认描述了一个不断发生的问题及这个问题的解决方案;模式是前人的设计经验上总结出来的对于一些普遍存在的问题提供的通用的解决方案;比如单例模式,观察者模式等;
- 软件中有很多模式:
面向对象的常见23中设计模式;
反模式;
企业应用架构模式等
《设计模式:可复用面向对象软件的基础(GOF)》
23种常见面向对象可复用
的设计模式
单例模式
- 有些时候,我们需要整个程序中有且只有一个实例
- 常见场景:如系统日志,Windows资源管理器窗口,数据库分配主键操作等;
- 实现思路:
1.Singleton拥有一个私有构造函数,确保用户无法通过new直接实例它;
2.包含一个静态私有变量instance与静态公有方法Instance();
代码实现
- Singleton.h
#include<iostream>
using namespace std;
class Singleton
{
public:
static const Singleton& getinstance();
static void doSomething()
{
cout<<"DO something!"<<endl;
}
//将构造函数和析构函数私有化,防止外部访问
private:
Singleton();
~Singleton();
static Singleton* This;//生命周期是全局区域,可见区域是类内。使用静态变量帮助解决资源的分配和释放
}
- Singleton.cpp
#include "Singleton.h"
Singleton* Singleton::This = NULL;
const Singleton* Singleton::getInstance()
{
if(!This)
{
This = new Singleton();
}
return This;
}
Singleton::Singleton()
{
}
Singleton::~Singleton()
{
}
- main.cpp
int main()
{
Singleton::getInstance()->doSomething();
}
static和单例模式的实现
静态对象只能调用静态方法,需要注意这一点。
观察者模式
Observer
- 在观察者模式中,观察者需要直接订阅目标事件;在目标发出内容改变的事件后,直接接收事件并作出响应,对象常是一对多关系;
- 常见场景:各种MVC的框架中,Model的变化通知各种类型的View时几乎都存在这种模式;
- 实现思路:将问题的职责解耦合,将Observable和Observer抽象开,分清抽象和实体;
Observer.h
#ifndef OBSERVER_H_
#define OBSERVER_H_
class Observer
{
public:
Observer(){};
virtual ~OBserver(){};
//当被观察对象发生变化时,通知被观察者调用这个方法
virtual void Update(void* pArg) = 0;//纯虚函数,子类实现
};
#endif
Observable.h
class Observer;//让编译器去工程中找这个类(但是这种写法可能存在问题,最好还是引入头文件)
#include<list>//双向链表
#include<string>
class Observerable
{
public:
Observerable();
virtual ~Observerable();
//注册观察者
void Attach(Observer* pOb);
//反注册观察者
void Detach(Observer* pOb);
int GetObserverCount() const
{
return _Obs.size();
}
void DetachAll()
{
_Obs.clear();
}
virtual void GetSomeNews(string str)
{
SetChange(str);
}
protected:
void SetChange();//有变化,需要通知
private:
void Notify(void* pArg);
private:
bool _bChange;
list<Observer*> _Obs;
}
Observable.cpp
#include"Observable.h"
Observable::Observable()
{
}
Observable::~Observable()
{
}
//注册观察者
Observable::void Attack(Observer* pOb)
{
if(pOb == NULL)
{
return;
}
//看看当前列表中是否有这个观察者
auto it = _Obs.begin();
for(;it != _Obs.end();it++)
{
if(*it == pOb)
return;
}
_Obs.push_back(pOb);
}
//反注册观察者
Observable::void Detach(Observer* pOb)
{
if((pOb == NULL) || (_Obs.empty() == true))
{
return;
}
_Obs.remove(pOb);
}
Observable::void SetChange(string news)
{
_bChange = true;
Notify((void*)news.c_str());
}
Observable::void Notify(void* pArg)
{
if(_bChange == false)
return;
//看看当前列表中是否有这个观察者
auto it = Obs.begin();
for(;it != _Obs.end();it++)
{
(*it)->Update(pArg);
}
_bChange = false;
}
main.cpp
#include"Observer.h"
#include<iostream>
using namespace std;
class News: public Observerable
{
public:
virtual void GetSomeNews(string str)
{
SetChange("News"+str);
}
}
class User1:public Observer
{
public:
void Update(void* pArg)//virtual子类可声明也就可以不声明
{
cout<<"User1 Got News"<<(char*)pArg<<endl;
}
}
class User2:public Observer
{
public:
void Update(void* pArg)//virtual子类可声明也就可以不声明
{
cout<<"User2 Got News"<<(char*)pArg<<endl;
}
}
int main()
{
User1 u1;
User2 u2;
News n1;
n1.GetSomeNews("T0");
cout<<n1.GetObserverCount()<<endl;//0
n1.Attach(&u1);
n1.Attach(&u2);
n1.GetSomeNews("T1");
cout<<n1.GetObserverCount()<<endl;//2
n1.Detach(&u2);
n1.GetSomeNews("T2");
cout<<n1.GetObserverCount()<<endl;//1
n1.DetachAll();
n1.GetSomeNews("T3");
cout<<n1.GetObserverCount()<<endl;//0
return 0;
}
void*、NULL和nullptr
- 在C语言中:
#define NULL((void*)0)
- 在C++语言中:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void*)0)
#endif
#endif
- 在C++11中,nullptr用来替代(void*)0,NULL则只表示0;
#include<iostream>
using namespace std;
void func(void* i)
{
cout<<"func(void* i)"<<endl;
}
void func(int i)
{
cout<<"func(int i)"<<endl;
}
int main()
{
int* pi = NULL;
int* pi2 = nullptr;
char* pc = NULL;
char* pc2 = nullptr;
func(NULL); //func(int i)
func(nullptr); //func(void* i)
func(pi); //func(void* i)
func(pi2); //func(void* i)
func(pc); //func(void* i)
func(pc2); //func(void* i)
return 0;
}
static_cast的使用
C的类型转换
C类型转换:
隐式类型转换:
比如:double f = 1.0/2;
显式类型转换:
(类型说明符)(表达式)
比如:double f = double(1)/double(2);
C类型转换的问题:
- 任意类型之间都可以转换,编译器无法判断其正确性;
- 难于定位,在源码中无法快速定位;
C++的类型转换
- const_cast:
用于转换指针或引用,去掉类型的const属性;
int main()
{
//C++ const转换
const int a = 10;
int* pA = const_cast<int*>(&a);
*pA = 100;//注意此时a的值也发生可改变
return 0;
}
- reinterpret_cast:
很危险!
重新解释类型,既不检查指向的内容,也不检查指针类型本身;但要求转换前后的类型所占用内存大小一致,否则将引发编译时错误。(应用较广泛,一般在指针类型的转换)
#include<iostream>
using namespace std;
int Test()
{
return 0;
}
int main()
{
typedef void(*FuncPtr)();
FuncPtr funcptr;
funcPtr = reinterpret_cast<FuncPtr>(&Test);
return 0;
}
- static_cast:
用于基本类型转换,有继承关系类对象和类指针之间转换,由程序员来确保转换是安全的,它不会产生动态转换的类型安全检查的开销;
#include<iostream>
using namespace std;
int main()
{
int i = 5;
double d = static_cast<double>(i);
double d2 = 5.6;
int i2 = static_cast<int>(d2);
return 0;
}
- dynamic_cast:
只能用于含有虚函数的类,必须用在多态体系中,用于类层次间的向上和向下转化;向下转化时,如果是非法的对于指针返回NULL;
#include<iostream>
using namespace std;
class Base
{
public:
Base(){}
virtual void T(){cout<<"Base:T"<<_i<<endl;}
private:
int _i;
};
class Derived:public Base
{
public:
Derived():_i(1){;}
virtual void T(){void T(){cout<<"Derived:T"<<_j<<endl;}}
private:
int _i;
}
int main()
{
Base cb;
Derived cd;
Base* pcb;
Derived* pcd;
//子类——>父类
pcb = static_cast<Base*>(&cd); //不会检查
pcb = dynamic_cast<Base*>(&cd); //会检查
if(pcb == NULL)
{
cout<<"unsafe dynamic_cast from Derived to Base"<<endl;
}
//父类——>子类
pcd = static_cast<Base*>(&cb); //不会检查
pcd = dynamic_cast<Base*>(&cb); //会检查
if(pcd == NULL)///会输出
{
cout<<"unsafe dynamic_cast from Base to Derived "<<endl;
}
}
注:父类转换为子类,可能不具有子类的方法和属性导致出错。
Adapter模式和多重继承
适配器(Adapter)模式
- 适配器将类接口转换为客户端期望的另一个接口;
- 使用适配器可防止类由于接口不兼容而一起工作;
- 适配器模式的动机是,如果可以更改接口,则可以重用现有软件;
#include<string>
#include<iostream>
using namespace std;
class LagcyRectangle
{
public:
LegacyRectangle(double x1,double y1,double x2,double y2)
{
_x1 = x1;
_y1 = y1;
_x2 = x2;
_y2 = y2;
}
void LegacyDraw()
{
cout<<"LegacyRectangle:LegacyDraw()"
<<_x1<<" "
<<_y1<<" "
<<_x2<<" "
<<_y2<<" "<<endl;
}
private:
double _x1;
double _y1;
double _x2;
double _y2;
};
class Rectangle
{
public:
virtual void Draw(string str) = 0;
};
//第一种适配的方式:使用多重继承
class RectangleAdapter:public Rectangle,public LagcyRectangle
{
public:
RectangleAdapter(double x,double y,double w,double h):
LagcyRectangle(x,y.x+w,y+h)
{
cout<<"RectangleAdapter(int x,int y,int w,int h)"<<endl;
}
virtual void Draw(string str)
{
cout<<"RectangleAdapter:DrawDraw()"<<endl;
LegacyDraw();
}
}
//组合方式的Adapter(面向对象方式很常见)
class RectangleAdapter:public Rectangle
{
public:
RectangleAdapter2(double x,double y,double w,double h):
_IRect(x,y.x+w,y+h)
{
cout<<"RectangleAdapter2(int x,int y,int w,int h)"<<endl;
}
virtual void Draw(string str)
{
cout<<"RectangleAdapter2:DrawDraw()"<<endl;
_IRect.Draw();
}
private:
LegacyRectangle _IRect;
};
int main()
{
double x= 20.0,y = 50.0,w = 300.0,h = 200.0;
RectangleAdapter ra(x,y,w,h);
Rectangle* pR = &ra;
pR->Drwa("Testing Adapter");
cout<<endl;
RectangleAdapter2 ra2(x,y,w,h);
Rectangle* pR2 = &ra2;
pR2->Drwa("Testing Adapter");
return 0;
}
设计模式总结
- 23种面向对象设计模式从分类上大致有创建型,结构型和行为型模式;
- 设计模式不能万能的,它建立在系统变化点上,哪里有变化哪里就可以用;
- 设计模式为了解耦合,为了扩展,它通常是演变过来的,需要演变才能准确定位;
- 设计模式是一种软件设计的方法,不是标准,面前大部分的框架中都已经包含了大量设计模式的思想;
泛型编程之泛型函数
泛型编程的思想
- 如果说面向对象是一种通过间接层来调用函数,以换取一种抽象,那么泛型编程则是更直接的抽象,它不会因为间接层而损失效率;(泛型编程效率高于面向对象)
- 不同于面向对象的动态期多态,泛型编程是一种静态期多态,通过编译器生成最直接的代码;(
模板编程的难点很大程度上在于对编译器的理解,我们需要知道怎么帮助编译器提供需要生成代码的信息;
) - 泛型编程可以将算法与特定类型,结构剥离,尽可能复用代码
template<class T>
#include<string.h>
T max(T a,T b)
{
return a>b?a:b;
}
template<class T1,class T2>
int max(T1 a,T2 b)
{
return static_cast<int>(a>b?a:b);
}
//特化
template<>
char* max(char* a,char* b)
{
return (strcmp(a,b)>0?(a):(b));
}
#include<iostream>
using namespace std;
int main()
{
cout<<max(1,2)<<endl;
cout<<max(1.5,3.5)<<endl;
cout<<max('a','b')<<endl;
cout<<max("hello","world")<<endl;
char* s1 = "hello";
char* s2 = "world";
cout<<max(s1,s2)<<endl;
cout<<max(10,2.5)<<endl;
return 0;
}
泛型编程的递推过程及总结
#include<iostream>
using namespace std;
//元编程:程序自动生成程序
template<int n>
struct Sum
{
enum Value{N = Sum<n-1>::N+n}; //Sum(n) = Sum(n-1) + n
};
template<>
struct Sum<1>
{
enum Value {N = 1};
}
int main()
{
cout<<Sum<100>::N<<endl; //事实上实在编译期计算的,不是在运行期计算的(效率最高效)
return 0;
}
总结
整理完咯!