高效地使用STL

有的公司用C++做后台服务器的不用STL,理由一般是低效。效率可能主要是和动态内存分配有关。可以用一些手段,使用STL的时候让系统在运行的过程中内存保持稳定:不出现运行时释放系统内存的行为。


通过举例子说明用STL保持运行时的内存稳定。

Role

        抽象一个游戏角色。

RoleMgr

         用一个hash_map管理所有的游戏角色,一个角色进入游戏的时候,RoleMgr就拿出一个Role对象。RoleMgr可以预先分配好所有的Role对象(当然是未初始化的),也可以运行的过程不断地创建Role对象,Role对象的内存空间肯定是稳定的,就是不删除重复利用。

RoleMgr重复利用内存规则,这个很多人肯定都是这样用的,但看到很多项目都用自己实现一套对象管理机制实现这一规则。


实现一个只有默认行为的Role:

  1 #include <iostream>
  2 #include <ext/hash_map>
  3 #include <ext/pool_allocator.h>
  4
  5 typedef uint32_t RoleID;
  6
  7 class Role
  8 {
  9 };


RoleMgr:

 11 class RoleMgr
 12 {
 13 public:
 14     Role* Create(const RoleID& roleID)
 15     {
 16         Role* role = m_roleAllocator.allocate(1);
 20         m_roleAllocator.construct(role, Role());
 21     //  ::new (role) Role;       
 22         m_roleHash.insert(std::make_pair(roleID, role));
 24
 25         return role;
 26     }
 27
 28     void Destroy(Role* role)
 29     {
 30         m_roleAllocator.deallocate(role, 1);
 31         m_roleAllocator.destroy(role);
 32     }
 33 private:
 34     typedef __gnu_cxx::__pool_alloc<Role> RoleAllocator;
 35     typedef __gnu_cxx::hash_map<RoleID, Role*, __gnu_cxx::hash<RoleID>,
 36         std::equal_to<RoleID>, __gnu_cxx::__pool_alloc<RoleID*> > RoleHash;
 37     RoleAllocator m_roleAllocator;
 39     RoleHash m_roleHash;
 40 }; 
把错误处理相关的代码给去了,一般server程序错误处理代码和正常逻辑的代码接近1:1,这样可以更好地定位bug。


Create就是从Allocator中取一块buff,然后用placement new,把Role对象放到这块内存里,同时调用Role 构造函数 & 拷贝构造函数。

Destroy把Create分配出来的内存放回到Allocator中,同时调用Role的析构函数。后面会讲不能调用这个这个析构函数。


RoleAllocator维护Role的内存,注意__pool_alloc<Role>,是Role,不是Role*。

hash_map的RoleID(key), Role*(value)也通过__pool_alloc管理。

这样只要RoleMgr生命还存在(析构函数没被调用?),RoleAllocator和RoleMgr里面的__pool_alloc内存是稳定的,不会被释放。


接下来,我们继续完善例子。现在的游戏都有道具任务宠物之类的乱七八槽的东西,把一个简单的道具加入到例子里。

Item

        道具的抽象。

ItemMgr

        管理道具对象。

ItemMgr和RoleMgr是一样的,RoleID换成ItemID, Role换成Item。可以一个通用模板类,ObjMgr:

template <class ObjID, class Type> class ObjMgr;


Role有了一个道具管理器:

 18 class Role
 19 {
 20 private:
 21     ItemMgr m_itemMgr;
 22 };


注意看RoleMgr::Destroy()

 31         m_roleAllocator.destroy(role);

Role的析构被调用了,ItemMgr的析构也被调用了,ItemMgr里各种Allocator也会被析构,Allocator的内存就被释放,这个是不允许的。

我们要做的是把把 31行去掉,项目成员们都要知道哪些类是不会调用析构函数的。


总结一下使用这篇文章的方法写代码时一些约束:

1. 只能用gnu g++较新的版本;

2. 类的成员不好用指针的形式;

3. 需要内存稳定的类的析构函数不执行;

4. 用Allocator::allocate出来的对象注意完全初始化。







评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值