【转载】C++11 实现一个超级对象池 正文

参考原文地址:(原创) 一个超级对象池的实现

我对文章的格式和错别字进行了调整,并补充标注出了重要的部分,代码也增加了详细注释。以下是正文。

正文

对象池对于创建开销比较大的对象来说很有意义,为了避免重复创建开销比较大的对象,我们可以通过对象池来优化。

对象池的思路比较简单,事先创建好一批对象,放到一个集合中,以后每当程序需要新的对象时候,都从对象池里获取,每当程序用完该对象后,都把该对象归还给对象池。这样会避免重复的对象创建,提高程序性能。

先来看看对象池的简单实现:

简单对象池的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
cpp复制代码#include <list>

template <typename Object>
class ObjectPool
{
public:
ObjectPool(size_t unSize) : m_unSize(unSize)
{
for (size_t unIdx = 0; unIdx < m_unSize; ++unIdx)
{
m_oPool.push_back(new Object());
}
}

~ObjectPool()
{
typename std::list<Object *>::iterator oIt = m_oPool.begin();
while (oIt != m_oPool.end())
{
delete (*oIt);
++oIt;
}
m_unSize = 0;
}

Object *GetObject()
{
Object *pObj = NULL;
if (0 == m_unSize)
{
pObj = new Object();
}
else
{
pObj = m_oPool.front();
m_oPool.pop_front();
--m_unSize;
}

return pObj;
}

void ReturnObject(Object *pObj)
{
m_oPool.push_back(pObj);
++m_unSize;
}

private:
size_t m_unSize;
std::list<object *> m_oPool;
};

这个 object pool 的实现很典型,初始创建一定数量的对象,取的时候就直接从池子中取,用完之后再回收到池子。一般的对象池的实现思路和这个类似,这种实现方式虽然能达到目的,但是存在以下不足

  1. 对象池 ObjectPool<T> 只能容纳特定类型的对象,不能容纳所有类型的对象,无法支持重载的和参数不同的构造函数;
  2. 对象用完之后需要手动回收,用起来不够方便,更大的问题是存在忘记回收的风险;

我希望能有一个更强大的对象池,这个对象池能容纳所有的对象,还能自动回收用完了对象,不需要手动回收,用起来更方便。要实现这样的对象池需要解决前面提到的两个问题,通过 C++11 就可以解决这两个问题。

  • 对于问题 1 :容纳所有的对象。本质上需要将对象池中的对象类型擦除,这里用 Any 类型就可以解决。
  • 对于问题 2 :自动回收用完的对象。这里用智能指针就可以解决,在创建智能指针时可以指定删除器,在删除器中不删除对象,而是回收到对象池中,而这个过程对外界来说是看不见的,由智能指针自己完成。

关于 Any 的实现见我前面的博客内容:C++11 打造好用的 Any

下面来看看超级对象池的具体实现吧。

超级对象池的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
cpp复制代码#include <string>
#include <functional>
#include <tuple>
#include <map>

#include "Any.hpp"

const int MaxObjectNum = 10;

class ObjectPool
{
template <typename T, typename... Args>
using Constructor = std::function<std::shared_ptr<T>(Args...)>;

public:
ObjectPool() : needClear(false)
{
}

~ObjectPool()
{
needClear = true;
}

//默认创建多少个对象
template <typename T, typename... Args>
void Create(int num)
{
if (num <= 0 || num > MaxObjectNum)
throw std::logic_error("object num errer");

auto constructName = typeid(Constructor<T, Args...>).name();

/// @note 用函数对象封装
Constructor<T, Args...> f = [constructName, this](Args... args)
{
return createPtr<T>(string(constructName), args...);
};

m_map.emplace(typeid(T).name(), f); ///< 存储函数对象

m_counter.emplace(constructName, num);
}

/// @note 返回智能指针
template <typename T, typename... Args>
std::shared_ptr<T> createPtr(std::string &constructName, Args... args)
{
/// 调用构造函数创建指针
return std::shared_ptr<T>(new T(args...), [constructName, this](T *t)
{///< Deleter 回收器
if (needClear)
delete[] t;
else
m_object_map.emplace(constructName, std::shared_ptr<T>(t)); ///< 放回对象池(存储对象指针)
});
}

template <typename T, typename... Args>
std::shared_ptr<T> Get(Args... args)
{
using ConstructType = Constructor<T, Args...>;

std::string constructName = typeid(ConstructType).name();
auto range = m_map.equal_range(typeid(T).name()); ///< 取得满足类型名的函数对象范围

for (auto it = range.first; it != range.second; ++it)
{
/// @note 取得范围中满足类型条件的函数对象
/// 继而利用它获取(或创建)对象指针
if (it->second.Is<ConstructType>())
{
auto ptr = GetInstance<T>(constructName, args...);

if (ptr != nullptr)
return ptr;

return CreateInstance<T, Args...>(it->second, constructName, args...);
}
}

return nullptr;
}

private:
template <typename T, typename... Args>
std::shared_ptr<T> CreateInstance(Any &any,
std::string &constructName, Args... args)
{
using ConstructType = Constructor<T, Args...>;
ConstructType f = any.AnyCast<ConstructType>();
/// @note 返回智能指针
return createPtr<T, Args...>(constructName, args...);
}

/// @note 初始化对象池
template <typename T, typename... Args>
void InitPool(T &f, std::string &constructName, Args... args)
{
int num = m_counter[constructName]; ///< 获取该类型需创建的对象个数

if (num != 0)
{
for (int i = 0; i < num - 1; i++)
{
m_object_map.emplace(constructName, f(args...)); ///< 直接构造对象并存储
}
m_counter[constructName] = 0;
}
}

/// @note 从对象池中获取对象
template <typename T, typename... Args>
std::shared_ptr<T> GetInstance(std::string &constructName, Args... args)
{
/// @note 寻找对象池中是否已经存有该对象
auto it = m_object_map.find(constructName);
if (it == m_object_map.end())
return nullptr;

/// @note 取出并转型该指针
auto ptr = it->second.AnyCast<std::shared_ptr<T>>();
if (sizeof...(Args) > 0)
*ptr.get() = std::move(T(args...));

m_object_map.erase(it); ///< 从对象池中除名该对象
return ptr;
}

private:
std::multimap<std::string, Any> m_map;
std::multimap<std::string, Any> m_object_map;
std::map<std::string, int> m_counter;
bool needClear;
};

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
cpp复制代码struct AT
{
AT() {}

AT(int a, int b) : m_a(a), m_b(b) {}

void Fun()
{
cout << m_a << " " << m_b << endl;
}

int m_a = 0;
int m_b = 0;
};

struct BT
{
void Fun()
{
cout << "from object b " << endl;
}
};

void TestObjectPool()
{
ObjectPool pool;
pool.Create<AT>(2);
pool.Create<BT>(2);
pool.Create<AT, int, int>(2);

{
auto p = pool.Get<AT>();
p->Fun();
}

auto pb = pool.Get<BT>();
pb->Fun();

auto p = pool.Get<AT>();
p->Fun();

int a = 5, b = 6;
auto p2 = pool.Get<AT>(a, b);
p2->Fun();

auto p3 = pool.Get<AT>(3, 4);
p3->Fun();

{
auto p4 = pool.Get<AT>(3, 4);
p4->Fun();
}
auto p4 = pool.Get<AT>(7, 8);
p4->Fun();
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%