-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
76b1b04
commit 25271d0
Showing
85 changed files
with
2,053 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
## CRTP模板编程(模板注入) | ||
|
||
### 概念 | ||
|
||
**CRTP**是`Curiously Recurring Template Pattern`的缩写,中文译为**奇异的递归模板模式**, 是派生类将自己作为模板参数传递给基类, 达到模板注入的效. 其优点是可以节约动态多态查找虚函数表的时间, 在编译阶段就实现静态多态. 定义方式类似于 | ||
|
||
```c++ | ||
template <class T> | ||
class Base | ||
{ | ||
// methods within Base can use template to access members of Derived | ||
}; | ||
class Derived : public Base<Derived> | ||
{ | ||
// ... | ||
}; | ||
``` | ||
###### 特点 | ||
- 继承自模板类 | ||
- 派生类将自身作为参数传给模板类 | ||
- 不用虚函数, **实质上是派生类重写同名函数时, 对基类函数的屏蔽** | ||
###### 颠倒继承 | ||
对于CRTP,是在**基类中调用派生类**的成员函数。而对于virtual虚函数,则是**派生类中调用基类**的成员函数,这就是我们所说的`颠倒继承`. | ||
### 实例 | ||
```c++ | ||
template <typename T> | ||
class Base { | ||
public: | ||
void interface() { | ||
static_cast<T*>(this)->imp(); | ||
}; | ||
}; | ||
class Derived : public Base<Derived> { | ||
public: | ||
void imp() { | ||
std::cout<< "in Derived::imp" << std::endl; | ||
} | ||
}; | ||
int main() { | ||
Derived b; | ||
d.interface(); | ||
return 0; | ||
} | ||
``` | ||
|
||
1. 这里面使用static_cast<>而不用dynamic_cast<>的原因是 dynamic_cast应用于运行时,而模板是在编译器就进行了实例化, 所以只能用static进行转换. | ||
2. 实质上, 这里在调用d.inerface() 的时候, 调用的是Base中的inerface(), 然后再通过static_cast转换为Derived指针进行多态函数的调用, 这个转换实际上是在编译阶段就已经做好了的 | ||
|
||
|
||
|
||
### 静态多态实例 | ||
|
||
```c++ | ||
#include <iostream> | ||
|
||
template <typename T> | ||
class Base{ | ||
public: | ||
void interface(){ | ||
static_cast<T*>(this)->imp(); | ||
//static_cast<>中只能使用&或*, 否则报错 | ||
T& temp = static_cast<T&>(*this); | ||
std::cout << typeid(temp).name() << std::endl; | ||
} | ||
|
||
void imp(){ | ||
std::cout << "in Base::imp" << std::endl; | ||
} | ||
}; | ||
|
||
class Derived1 : public Base<Derived1> { | ||
public: | ||
void imp(){ | ||
std::cout << "in Derived1::imp" << std::endl; | ||
} | ||
}; | ||
|
||
class Derived2 : public Base<Derived2> { | ||
public: | ||
void imp(){ | ||
std::cout << "in Derived2::imp" << std::endl; | ||
} | ||
}; | ||
|
||
struct Derived3 : public Base<Derived3>{}; | ||
|
||
template <typename T> | ||
void fun(T& base){ | ||
base.interface(); | ||
} | ||
|
||
int main(){ | ||
Derived1 d1; | ||
Derived2 d2; | ||
Derived3 d3; | ||
|
||
fun(d1); | ||
fun(d2); | ||
fun(d3); | ||
} | ||
``` | ||
在调用fun的时候, 首先将模板T转换为对应的派生类类型, 然后调用派生类中, 从基类继承下来的interface函数, 在通过interface中的static_cast转换为派生类指针, 从而表现出虚函数. | ||
注意: | ||
被递归模板模式使用的基类模板`base`中,不能使用模板参数类型T的对象,**只能使用T的指针或引用**,因为在base中,T的类型是不完整的,因为T还要继承自`base`。 | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
## 模板工厂 | ||
|
||
### 简单版本 | ||
|
||
#### 简介: | ||
|
||
通过模板生成不同基类产品的工厂,再通过基类的产品工厂的第二个模板参数,指定具体的派生类产品,创建出来产品后返回基类的指针,在调用时通过多态来展现出不同的产品特性。 | ||
|
||
#### 优缺点: | ||
|
||
优点:运行快,主要在编译阶段创建工厂,且抽象度高,代码可复用程度高 | ||
|
||
缺点:每次使用时都需要创建工厂,有浪费。 | ||
|
||
#### 示例 | ||
|
||
产品类 | ||
|
||
```c++ | ||
// 基类 鞋子 | ||
class Shoes | ||
{ | ||
public: | ||
virtual void Show() = 0; | ||
virtual ~Shoes() {} | ||
}; | ||
|
||
// 耐克鞋子 | ||
class NiKeShoes : public Shoes | ||
{ | ||
public: | ||
void Show() | ||
{ | ||
std::cout << "我是耐克球鞋,我的广告语:Just do it" << std::endl; | ||
} | ||
}; | ||
``` | ||
|
||
模板工厂 | ||
|
||
```c++ | ||
// 抽象模板工厂类 | ||
// 模板参数:AbstractProduct_t 产品抽象类 | ||
template <class AbstractProduct_t> | ||
class AbstractFactory | ||
{ | ||
public: | ||
virtual AbstractProduct_t *CreateProduct() = 0; | ||
virtual ~AbstractFactory() {} | ||
}; | ||
|
||
// 具体模板工厂类 | ||
// 模板参数:AbstractProduct_t 产品抽象类,ConcreteProduct_t 产品具体类 | ||
template <class AbstractProduct_t, class ConcreteProduct_t> | ||
class ConcreteFactory : public AbstractFactory<AbstractProduct_t> | ||
{ | ||
public: | ||
AbstractProduct_t *CreateProduct() | ||
{ | ||
return new ConcreteProduct_t(); | ||
} | ||
}; | ||
``` | ||
|
||
main函数 | ||
|
||
```c++ | ||
int main() | ||
{ | ||
// 构造耐克鞋的工厂对象 | ||
ConcreteFactory<Shoes, NiKeShoes> nikeFactory; | ||
// 创建耐克鞋对象 | ||
Shoes *pNiKeShoes = nikeFactory.CreateProduct(); | ||
// 打印耐克鞋广告语 | ||
pNiKeShoes->Show(); | ||
|
||
// 释放资源 | ||
delete pNiKeShoes; | ||
pNiKeShoes = NULL; | ||
|
||
return 0; | ||
} | ||
``` | ||
|
||
|
||
|
||
### 模板类+单例类工厂 | ||
|
||
#### 简介: | ||
|
||
解决简单版本不能随时统一获取指定产品的缺点,增加一个单例工厂,用map保存所有的实例产品。 | ||
|
||
- 把产品注册的功能封装成产品注册模板类Register。注册的产品对象保存在工厂模板类Factory的**std::map**中,便于产品对象的获取。 | ||
- 把获取产品对象的功能封装成工厂模板类。为了能随时随地获取指定产品对象,则把工厂设计成单例模式。 | ||
|
||
#### 优缺点: | ||
|
||
优点:运行快,实例化创建都是在编译阶段进行的,且抽象度高,复用程度好,做到了开闭原则,且扩展性和封装程度都很好。 | ||
|
||
缺点:容易让人读不懂,代码复杂。 | ||
|
||
#### 示例 | ||
|
||
产品类不变 | ||
|
||
产品注册类Register和单例工厂 | ||
|
||
```c++ | ||
// 基类,产品注册模板接口类 | ||
// 模板参数 ProductType_t 表示的类是产品抽象类 | ||
template <class ProductType_t> | ||
class IProductRegistrar | ||
{ | ||
public: | ||
// 获取产品对象的抽象接口 | ||
virtual ProductType_t *CreateProduct() = 0; | ||
|
||
protected: | ||
// 禁止外部构造和虚构, 子类的"内部"的其他函数可以调用 | ||
IProductRegistrar() {} | ||
virtual ~IProductRegistrar() {} | ||
|
||
private: | ||
// 禁止外部拷贝和赋值操作,否则容易造成悬空指针或者内存泄漏 | ||
IProductRegistrar(const IProductRegistrar &); | ||
const IProductRegistrar &operator=(const IProductRegistrar &); | ||
}; | ||
|
||
// 工厂模板类,用于获取和注册产品对象 | ||
// 模板参数 ProductType_t 表示的类是产品抽象类 | ||
template <class ProductType_t> | ||
class ProductFactory | ||
{ | ||
typedef IProductRegistrar<ProductType_t> RegisteryBase; | ||
|
||
public: | ||
// 获取工厂单例,特定工厂的实例是唯一的 | ||
static ProductFactory<ProductType_t> &Instance() | ||
{ | ||
static ProductFactory<ProductType_t> instance; | ||
return instance; | ||
} | ||
|
||
// 产品注册 | ||
void RegisterProduct(RegisteryBase *registrar, std::string name) | ||
{ | ||
m_ProductRegistry[name] = registrar; | ||
} | ||
|
||
// 根据名字name,获取对应具体的产品对象 | ||
ProductType_t *GetProduct(std::string name) | ||
{ | ||
// 从map找到已经注册过的产品,并返回产品对象 | ||
if (m_ProductRegistry.find(name) != m_ProductRegistry.end()) | ||
{ | ||
return m_ProductRegistry[name]->CreateProduct(); | ||
} | ||
else{ | ||
std::cout << "No product found for " << name << std::endl; | ||
return NULL; | ||
} | ||
} | ||
|
||
private: | ||
// 禁止外部构造和虚构 | ||
ProductFactory() {} | ||
~ProductFactory() {} | ||
|
||
// 禁止外部拷贝和赋值操作 | ||
ProductFactory(const ProductFactory &); | ||
const ProductFactory &operator=(const ProductFactory &); | ||
|
||
// 保存注册过的产品,key:产品名字 , value:产品类型 | ||
std::map<std::string, RegisteryBase *> m_ProductRegistry; | ||
}; | ||
|
||
// 产品注册模板类,用于创建具体产品和从工厂里注册产品 | ||
// 模板参数 ProductType_t 表示的类是产品抽象类(基类),ProductImpl_t 表示的类是具体产品(产品种类的子类) | ||
template <class ProductType_t, class ProductImpl_t> | ||
class ProductRegistrar : public IProductRegistrar<ProductType_t> | ||
{ | ||
public: | ||
// 构造函数,用于注册产品到工厂,只能显示调用 | ||
explicit ProductRegistrar(std::string name) | ||
{ | ||
// 通过工厂单例把产品注册到工厂 | ||
ProductFactory<ProductType_t>::Instance().RegisterProduct(this, name); | ||
} | ||
|
||
// 创建具体产品对象指针 | ||
ProductType_t *CreateProduct() | ||
{ | ||
return new ProductImpl_t(); | ||
} | ||
}; | ||
``` | ||
main函数 | ||
```c++ | ||
int main() | ||
{ | ||
// 注册产品种类为Shoes(基类),产品为NiKe(子类)到工厂,产品名为nike | ||
ProductRegistrar<Shoes, NiKeShoes> nikeShoes("nike"); | ||
Shoes *pNiKeShoes = ProductFactory<Shoes>::Instance().GetProduct("nike"); | ||
pNiKeShoes->Show(); | ||
// 释放资源 | ||
if (pNiKeShoes) | ||
{ | ||
delete pNiKeShoes; | ||
} | ||
return 0; | ||
} | ||
``` | ||
|
Oops, something went wrong.