C++-C++ 容器中指针所指向的资源的释放问题?

需求定制 需求定制 主题:1050 回复:2283

C++-C++ 容器中指针所指向的资源的释放问题?

想挽留 发布于 2017-07-11 字数 433 浏览 1346 回复 4

在项目中经常会用到STL中的容器,比如vector或list等,而且很多时候必须在其中存储指针类型,这就出现了一个问题,容器中的指针所指向的对象如何析构?由于很多情况下,还必须是原生指针,所以不讨论使用智能指针的情况。

class Resource

class ResourceManager
{
...
private:
typedef std::vector<Resource*> Resources_t;

Resources_t m_resources;
};

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

支持 Markdown 语法,需要帮助?

评论(4

灵芸 2017-10-16 4 楼

对于C++11,可以使用

 typedef std::vector<std::unique_ptr<Resource> > Resources_t;

来管理你的resource。
也可以使用boost的Pointer Container Library:

 #include <boost/ptr_container/ptr_vector.hpp>

typedef boost::ptr_vector<Resource> Resources_t;

泛泛之交 2017-09-07 3 楼

解决这个问题有一个看起来不错的方法,简单且有效:

void del_Resource(Resource *r); // 这个函数释放r所指向的资源。

void ResourceManager::~ResourceManager()
{
std::for_each(m_resource.begin(), m_resources.end(), del_Resource);
}

但是有可能遇到比较麻烦的问题,加入我的ResourceManager中有10个这样的vector,就必须在析构函数中调用10次for_each,这对于代码维护不是好事。

这是一种RRID(资源释放即析构)机制,是比RAII低一层次的封装,但是往往很有用。

解决上面的问题需要对容器进行一层包装,直接看代码吧:

 template< typename C, typename F >

class sequence_container_veneer : public C
{
public:
~sequence_container_veneer()
{
// 使用F()来删除所有剩余的元素
std::for_each(begin(), end(), F());
}
...
};

struct RelResource
{
void operator () (Resource *r)
{
del_Resource(r); // 这里释放指针r所对应的资源
}
};

class ResourceManager
{
private:
typedef std::vector<Resoure*> Resources_t;
typedef sequence_container_veneer< Resources_t, RelResource > Resource_vec_rrid_t;

 Resource_vec_rrid_m_resources;

}

void ResourceManager::~ResourceManager()
{
// 现在这个析构函数无事可干了
}

类模版sequence_container_veneer将它所要包覆的容器类型作为模版参数,另一个模版参数是一个仿函数,用于资源清理。
改进后的容器可以自动完成你本来希望在析构函数中做的事情,这样就免除了手工完成可能导致的错误。

偏爱自由 2017-07-31 2 楼

如果对效率不是特别特别敏感话用智能指针代替

泛泛之交 2017-07-28 1 楼

当容器中存放的是指针时,释放资源的时候必须首先逐个释放掉指针所指向的资源,然后才能删除容器,否则会造成资源泄露。
但是尽量不要用vector存放指针,因为Vector存储的是拷贝,而对于指针来说,多一个备份就意味着多一份危险。

下面是一段摘自《C++编程规范》的文字,可以参考下:

第79条 在容器中只存储值和智能指针

概要
在容器中存储值对象: 容器假设它们存储类值类型,包括值类型(直接持有),智能指针,以及迭代器。

讨论
容器的最常见用法是存储直接持有的值(譬如,vector<int>, set<string>等)。对于指针的容器: 如果容器拥有所指向的对象,提倡使用包含引用计数的智能指针容器(比如,list<shared_ptr<Widget> >); 否则,裸指针容器(譬如list<Widget*>)或者像迭代器那样的类指针的值(譬如list< vector<Widget>::iterator >)也行。

实例
例1: auto_ptr. 由于其转移所有权的复制语义,auto_ptr<T>的对象不是类值的。使用auto_ptr的容器(例如vector< auto_ptr<int> >)应编译失败。就算能编译成功,也不要那样用。如果你真的想要,你几乎肯定是想要一个相应的shared_ptr的容器。
例2: 异质容器。要想有一个容器存储并拥有不同但是类型相关的对象,比如从一个公共基类Base继承得到的类型,提倡使用container< shared_ptr<Base> >. 一种替代方案是存储代理对象,其非虚拟函数穿越到实际对象的对应虚拟函数。
例3: 非值类型容器。即使要存储那些不可赋值也就是非类值的对象(例如数据库锁或者网络连接),提倡通过智能指针(例如container< shared_ptr<DatabaseLocks> >和container< shared_ptr<TcpConnection> >)来间接包含它们。
例4: 可选值。当你想要一个map<Thing, Widget>,但是有的Thing没有相关Widget的时候,提倡使用map<Thing, shared_ptr<Widget> >.
例5: 索引容器。要想用一个主容器持有对象,然后使用不同的序关系来访问又不必重新排序主容器,你可以建立辅助容器“指向”主容器中的内容,并以不同方式使用解引用的比较谓词来排序辅助容器。提倡使用MainContainer::iterator(这是类值的)而不是指针的容器。

关于STL容器里存储对象还是指针的抉择可以参考:
STL容器里存放对象还是指针