设计模式 描述了一些在编程的过程中不断重复出现的 典型问题 及这些问题的 核心解决方案。
GoF设计模式中收录了23种设计模式,根据功能,一般分三类:
创建型模式
单例(件)模式、工厂模式、抽象工厂模式等
结构型模式
适配器模式、桥接模式、代理模式等
行为模式
迭代器模式、观察者模式、命令模式等
单例模式 Singleton
保证一个类只有一个实例, 并提供一个访问该实例的全局接口.
一般思路:
1. 隐藏构造函数(私有或保护), 删除拷贝操作函数.
2. 提供一个公有的静态成员函数, 以获取这个唯一的实例
例:
class Singleton
{
public:
// 全局访问接口
static Singleton *instance()
{
// 静态局部变量, 从C++11起, 保证线程安全
static Singleton instance(0);
return &instance;
}
void setValue(int x)
{
_value = x;
}
int value() const
{
return _value;
}
private:
// 隐藏构造函数
Singleton(int x) : _value(x) {}
// 删除拷贝操作
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
int _value;
};
注:
单例模式根据这个唯一实例的创建时机, 可分两种:
惰性初始化(懒汉式)
第一次调用instance函数时,才创建这个唯一的对象
预先初始化(饿汉式)
只要加载单例类型, 就会自动创建这个唯一实例
迭代器模式
提供一种方法顺序访问聚合对象中的各元素,同时不暴露聚合对象的内部实现。
核心思想:
将聚合对象的 访问与遍历 从 聚合对象中 分离出来,放到一个迭代器中。
C++标准库的一般做法:
1、每个聚合对象提供自己的迭代器类型(一般使用嵌套类型)
2、迭代器中提供一组简单但通用的接口,使用运算符实现,如 * ++ !=
3、每个聚合对象 还需要提供两个专门的接口:
begin 用于获取聚合对象中 首元素的迭代器
end 用于获取聚合对象的 尾后迭代器(即最后一个元素后面那个位置)
例:
class string
{
public:
// 嵌套的迭代器类型
class iterator
{
public:
iterator(char* p): p(p) {}
char& operator*(){
return *p;
}
iterator& operator++(){
++p;
return *this;
}
bool operator!=(const iterator& other) const {
return this->p != other.p;
}
private:
char* p;
};
// 两个特殊的接口
iterator begin(){
return iterator(_data);
}
iterator end(){
return iterator(_data+this->size());
}
...
};
典型使用方式:
string s1 = "hello world";
for (auto it = s1.begin(); it != s1.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
适配器 Adapter
将一个类的接口转换成客户希望的另外一个接口。
适配器使得原本由于不兼容而不能一起工作的类可以一起工作。
适配器也叫 包装器(Wrapper)。
C++标准库中的栈和队列使用的就是适配器模式实现的。
包装动态数组实现顺序栈,示例:
#include "myvector.hpp"
template<typename T>
class MyStack
{
public:
void push(T data)
{
c.push_back(data);
}
void pop()
{
c.pop_back();
}
T top() const
{
return c.back();
}
bool empty() const
{
return c.empty();
}
int size() const
{
return c.size();
}
private:
MyVector<T> c;
};