公司另一个项目组的同事,昨天做了讲座:一个野指针的处理方案。
先看一个典型的野指针:
#include <iostream>
#include <string>
using namespace std;
//===========================================================
class Player
{
string _name;
public:
Player(const string& name)
:_name(name)
{
}
const string& Name() const
{
return _name;
}
};
//===========================================================
class Pet
{
string _name;
Player* _player;
public:
Pet(const string& name, Player* player)
:_name(name)
,_player(player)
{
}
void Update()
{
cout << _name << " loves " << _player->Name() << "!\n";
}
};
//===========================================================
int main()
{
Player* tom = new Player("Tom");
Pet* pet = new Pet("Luna", tom);
pet->Update();
delete tom;
pet->Update();
//-------------------------------
cout <<"------- Done -------";
cin.get();
return 0;
}
在 Tom析构后,pet中就剩下了野指针,它访问的是已经回收的内存。
该同事介绍的方案是“内存池”,用以代替std::shared_ptr。用他的话说,智能指针会加锁,所以在高并发的时候会产生瓶颈。(我有疑问:这种情况下,不是应该用weak_ptr吗?weak_ptr不会加锁吧?)
内存池的方案果然不错,在运行时效率很高。看下面的代码:
poolmgr.h 该文件定义了两个模板:
- PoolManaged用于包装Player,把Player的内存管理权交于池。
- PoolPtr用于Pet,相当于一个弱指针。
#include <boost/pool/pool.hpp> // requires boost::thread lib
template <class Entity, class... Args> // use "parameter pack" to accept zero or more template arguments, aka., variadic template
class PoolManaged final: public Entity
{
public:
PoolManaged(Args... args)
: Entity(args...)
, _guid(++GUID_SEED)
{ /* empty */ }
~PoolManaged() {_guid = 0;}
int guid() const {return _guid;}
// memory allocation and free is delegated to boost::pool
static void * operator new(std::size_t size) throw(std::bad_alloc)
{
return mempool().malloc();
}
static void operator delete(void *mem, std::size_t size) throw()
{
return mempool().free(mem);
}
private:
int _guid; // Globally Unique ID: to identify pool's objects
static int GUID_SEED;
static boost::pool<>& mempool()
{
static boost::pool<> sp(sizeof(PoolManaged<Entity, Args...>));
return sp;
}
};
template<class Entity, class... Args>
int PoolManaged<Entity, Args...>::GUID_SEED = 0;
// the user of above template
template<class PoolManaged>
class PoolPtr
{
public:
PoolPtr(PoolManaged* ref)
: _ref(ref), _refGuid(ref->guid())
{ /* empty */ }
// check if currently-refencing object is the old one
bool isValid() const {return _refGuid == _ref->guid();}
PoolManaged* operator ->() const
{
assert(_refGuid == _ref->guid());
return _ref;
}
private:
PoolManaged* _ref;
int _refGuid;
};
在使用时很简单,定义两个符号即可:
#include "poolmgr.h"
#include <iostream>
using namespace std;
class Player
{
public:
Player(const string& name) : _name(name) { /* empty */ }
const string& name() {return _name;}
private:
string _name;
};
typedef PoolManaged<Player, string> PoolPlayer; // use pool to manage Player objects
typedef PoolPtr<PoolPlayer> PoolPlayerWeakPtr; // use weak ptr to reference a pool's entity
class Pet
{
public:
Pet(PoolPlayer* owner)
: _player(owner)
{ /* empty */ }
void update()
{
if (_player.isValid())
{
cout << _player->name() << endl;
}
else
{
cout << "owner is lost.\n";
}
}
private:
PoolPlayerWeakPtr _player;
};
int _tmain(int argc, _TCHAR* argv[])
{
PoolPlayer* p = new PoolPlayer("Tom");
Pet dog(p);
dog.update();
delete p;
dog.update();
PoolPlayer* p2 = new PoolPlayer("Jerry");
dog.update();
return 0;
}
PoolPlayer与PoolPlayerWeakPtr即可完成这项“野指针”的监督任务。