深入理解C++11(十二)

深入理解C++11(十二)

改进对象池模式
对象池对于创建开销比较大的对象来说很有意义,为了避免重复创建开销比较大的对象,可以通过对象池来优化。对象池的思路比较简单,事先创建好一批对象,放到一个集合中,每当程序需要新的对象时,就从对象池中获取,程序用完该对象后都会把该对象归还给对象池。这样会避免重复创建对象,提高程序性能。对象池的实现如代码清单8-12所示。

#include<string>
#include<functional>
#include<memory>
#include<map>
using namespace std;
const int MaxObjectNum = 10;
template<typename T>
class ObjectPool
{
template<typename... Args>
using Constructor = std::function<std::shared_ptr<T>(Args...)>;
public:
ObjectPool() : needClear(false){}
~ObjectPool()
{
needClear = true;
}
// 默认创建多少个对象
template<typename... Args>
void Init(size_t num, Args&&... args)
{
if (num <= 0 || num> MaxObjectNum1)
throw std::logic_error("object num out of range.");
auto constructName = typeid(Constructor<Args...>).name(); // 不区分引用
for (size_t i = 0; i <num; i++)
{
m_object_map.emplace(constructName, shared_ptr<T>(new T(std::forward
<Args>(args)...), [this, constructName](T* p)
// 删除器中不直接删除对象,而是回收到对象池中,以供下次使用
{
return createPtr<T>(string(constructName), args...);
}));
}
}
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)
{
if (needClear)
delete[] t;
else
m_object_map.emplace(constructName, std::shared_ptr<T>(t));
});
}
// 从对象池中获取一个对象
template<typename... Args>
std::shared_ptr<T> Get()
{
string constructName = typeid(Constructor<Args...>).name();
auto range = m_object_map.equal_range(constructName);
for (auto it = range.first; it != range.second; ++it)
{
auto ptr = it->second;
m_object_map.erase(it);
return ptr;
}
return nullptr;
}
private:
std::multimap<std::string, std::shared_ptr<T>> m_object_map;
bool needClear;
};

这个对象池的实现很典型:初始创建一定数量的对象,用的时候直接从
池中取,用完之后再回收到池子。

一般对象池的实现思路和这个类似,这种实现方式虽然能达到目的,但是存在以下不足:

1)对象用完之后需要手动回收,用起来不够方便,更大的问题是存在忘
记回收的风险。
2)不支持参数不同的构造函数。

通过C++11可以解决这两个问题:

1)对于第一个问题,通过自动回收用完的对象来解决。这里用智能指针
就可以解决,在创建智能指针时可以指定删除器,在删除器中不删除对象,
而是将其回收到对象池中。这个过程对外界来说是看不见的,由智能指针自
己完成。

2)对于第二个问题,通过可变参数模板来解决。可变参数模板可以支持
不同参数的构造函数来创建对象。

对象池的实现如代码清单8-13所示:

#include<string>
#include<functional>
#include<memory>
#include<map>
#include "NonCopyable.hpp"
usingnamespace std;
const int MaxObjectNum = 10;
template<typenameT>
classObjectPool : NonCopyable
{
template<typename... Args>
using Constructor = std::function<std::shared_ptr<T>(Args...)>;
public:
// 默认创建多少个对象
template<typename... Args>
void Init(size_tnum, Args&&... args)
{
if (num<= 0 || num> MaxObjectNum)
throw std::logic_error("object num out of range.");
auto constructName = typeid(Constructor<Args...>).name(); // 不区分引用
for (size_t i = 0; i <num; i++)
{
m_object_map.emplace(constructName, shared_ptr<T>(newT(std::
forward<Args>(args)...), [this, constructName](T* p)
// 删除器中不直接删除对象,而是回收到对象池中,以供下次使用
{
m_object_map.emplace(std::move(constructName), std::shared_ptr<T>(p));
}));
}
}
// 从对象池中获取一个对象
template<typename... Args>
std::shared_ptr<T> Get()
{
string constructName = typeid(Constructor<Args...>).name();
auto range = m_object_map.equal_range(constructName);
for (auto it = range.first; it != range.second; ++it)
{
auto ptr = it->second;
m_object_map.erase(it);
return ptr;
}
return nullptr;
}
private:
multimap<string, std::shared_ptr<T>> m_object_map;
};

测试代码如代码清单8-14所示:

#include <iostream>
struct BigObject
{
BigObject(){}
BigObject(int a){}
BigObject(const int& a, const int& b){}
void Print(const string& str)
{
cout <<str<< endl;
}
};
void Print(shared_ptr<BigObject>p, const string& str)
{
if (p != nullptr)
{
p->Print(str);
}
}
void TestObjPool()
{
ObjectPool<BigObject> pool;
pool.Init(2); // 初始化对象池,初始创建两个对象
{
auto p = pool.Get();
Print(p, "p");
auto p2 = pool.Get();
Print(p2, "p2");
}// 出了作用域之后,对象池返回出来的对象又会自动回收
auto p = pool.Get();
auto p2 = pool.Get();
Print(p, "p");
Print(p2, "p2");
// 对象池支持重载构造函数
pool.Init(2, 1);
auto p4 = pool.Get<int>();
Print(p4, "p4");
pool.Init(2, 1, 2);
auto p5 = pool.Get<int, int>();
Print(p5, "p5");
}
int main()
{
TestObjPool ();
return 0;
}

输出结果如下:
p
p2
p
p2
p4
p5

在上述测试代码中,设置了对象池的最大容量为2,所以在获取p3时,将获得一个空指针。在离开作用域之后对象就被对象池回收,后面就能再获取池中的对象了。这里需要注意的是,对象被回收之后它的状态并没有被清除,用户从池中获取对象之后最好先初始化或重置一下状态。p4和p5是不同构造函数创建的对象。需要注意的是,在对象池中创建不同的对象时,构造函数入参的引用将被忽略,即入参int和int&的构造函数会被认为是同一种类型的构造函数,因为在构造对象时无法获取变参Args…的引用类型,丢失了引用相关的信息。

相比传统的实现方式,改进之后的对象池的实现不仅能自动回收对象,还能支持参数不同的构造函数,更加灵活和强大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值