这是尝试使用STL算法而不是手写循环等来重写一些旧作业。
我有一个名为Database的类,它包含Vector<Media *>
,其中Media *可以是(或其他)CD或书。数据库是唯一处理动态内存的类,当程序启动时,它会读取一个格式如下所示的文件(稍微简化),在读取条目时分配空间,将它们添加到上面的向量(v_)。
CD Artist Album Idnumber Book Author Title Idnumber Book ... ...
使用手写循环时,删除这些对象可以正常工作:编辑:对不起我说得太快,实际上并不是一个'手写'循环本身...我一直在编辑项目以删除手写的循环,这实际上使用find_if算法和手动删除,但问题是有效的。 /编辑。
typedef vector<Media *>::iterator v_iter;
... void Database::removeById(int id) { v_iter it = find_if(v_.begin(), v_.end(), Comparer(id)); if (it != v_.end()) { delete *it; v_.erase(it); } }
这应该是非常明显的 - 如果找到与参数匹配的id,则仿函数返回true,并且对象被销毁。这工作和valgrind报告没有内存泄漏,但由于我想使用STL,显而易见的解决方案应该是使用擦除删除习惯用法,导致如下所示
void Database::removeById(int id) { v_.erase(remove_if(v_.begin(), v_.end(), Comparer(id)), v_.end()); };
然而,这个“有效”但根据valgrind导致内存泄漏,那么是什么给出了?第一个版本工作正常,没有任何泄漏 - 而这个版本总是显示3个allocs'not freed'为我删除的每个Media对象。
答案 0 :(得分:4)
规则是:如果对包含并且是指针所有者的向量应用remove(),则会泄漏内存。
Scott Meyers的“Effective STL”有一个很好的解决方案。它不涉及任何智能指针!仅使用智能指针进行擦除删除工作是一种矫枉过正的行为。
这是(来自书,第33项)。任务是有选择地从isCertified()返回false的矢量小部件中删除。
class Widget
{
public:
isCertified() const;
...
};
void delAndNullifyUncertified(Widget*& pWidget)
{
if (!pWidget->isCertified())
{
delete pWidget;
pWidget = 0;
}
}
vector<Widget *> v;
v.push_back(new Widget);
...
// set to NULL all uncertified widgets
for_each(v.begin(), v.end(), delAndNullifyUncertified);
// eliminate all NULL pointers from v
v.erase(remove(v.begin(), v.end(), static_cast<Widget *>(0)),
v.end();
我希望这会有所帮助。
编辑(2012年8月28日):反映来自Slavik81的有效观察
答案 1 :(得分:3)
在第一个版本中,您正在小心地呼叫delete *it
。在更新版本中,您不是.... v_.erase
正在取消分配指针,而不是指针引用的对象。
答案 2 :(得分:3)
这就是为什么你应该总是使用智能指针。您遇到问题的原因是因为您使用了哑指针,将其从向量中移除,但是没有触发释放它指向的内存。相反,你应该使用一个智能指针,它总是释放指向内存,从向量中移除等于释放指向内存。
答案 3 :(得分:2)
你已经have a specific answer了。但是,您的根本问题是您使用裸指针手动管理资源。这很难,有时会很痛
将您的类型更改为std::vector<std::shared_ptr<Media> >
,事情变得更加容易。
(如果您的编译器尚不支持std::shared_ptr
,则很可能会std::tr1::shared_ptr
。否则请使用boost's boost::shared_ptr
。)
答案 4 :(得分:1)
delete
的第二版中没有removeById
的来电。从指针向量中删除元素不会在指针上调用delete
。
提供的删除删除版本大致相当于使用原始removeById
,但只做了一些小改动:
void Database::removeById(int id) {
v_iter it = find_if(v_.begin(), v_.end(), Comparer(id));
if (it != v_.end()) {
//delete *it;
v_.erase(it);
}
}
希望这样可以更清楚地了解发生了什么,以及泄漏出现的原因。
答案 5 :(得分:1)
显然,你的vector
OWNS包含它所包含的对象。
正如您所注意到的那样,STL容器在它们不容易适应继承的意义上不是OO友好的。你需要使用动态分配的内存及其所有的麻烦。
第一个简单的解决方案是用智能指针替换普通指针(请不要再用)。人们通常会推荐shared_ptr
,但是如果您有权访问C ++ 0x则更喜欢unique_ptr
。
您还应该考虑另一种解决方案。容器模仿STL,但设计用于继承层次结构。他们在引擎盖下使用指针(很明显),但让你自己处理内存管理和与智能指针相关的开销(特别是shared_ptr
)。
看看Boost Pointer Container图书馆!
这显然是最好的解决方案,因为它是针对您要解决的问题创建的。
此外,它的容器(我认为)符合STL标准,因此您可以使用erase/remove
成语,find_if
算法等...
答案 6 :(得分:0)
这意味着第二个版本不正确。如果您想使用它,请考虑shared_ptr:
typedef vector< shared_ptr<Media> > MediaVector; ...