我们首先看个例子。
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
class Widget
{
public:
Widget(int a) : m_a(a)
{
if(a % 2 == 0)
{
m_isMeasured = true;
}
}
bool isMeasured()
{
return m_isMeasured;
}
private:
int m_a {0};
bool m_isMeasured {false};
};
int main()
{
typedef vector<Widget*> WidgetVec;
typedef vector<Widget*>::iterator WidgetIter;
WidgetVec v;
for(int index = 0; index < 10; ++index)
{
v.push_back(new Widget(index));
}
for(WidgetIter i = v.begin(); i != v.end(); ++i)
{
cout << (*i)->isMeasured() << " "; // 1 0 1 0 1 0 1 0 1 0
}
return 0;
}
上面的例子中,我们首先定义一个类Widget
,然后根据构造的时候的数值对成员变量m_isMeasured
进行赋值。
接下来我们定义一个vector
容器,用来存储创建的Widget
指针。
当我们做了很多不知道为什么的操作之后,不知道是哪根筋搭错了,我就想把容器中对于没有被标记measured
的对象删除。
而通常的方法我们就是选择自己写循环,对每个成员进行删除。但是不由得又想起来以前好像模糊中说过的,尽量使用算法而不是编写显示的循环。
刚好,我们刚刚才在上一节中学习了erase-remove
方法来删除容器的元素。也就很顺利的用来解决问题。
v.erase(remove_if(v.begin(), v.end(), not1(mem_fun(&Widget::isMeasured)));
不知道 not1
和 mem_fun
可以自行百度查询一下定义和用法。
但是前面我们好像有说过,删除容器中的指针并不能删除指针所指的对象,因此使用erase
删除容器中的指针的时候,可能会造成内存泄漏。
但是在上面的这行代码中,你的担忧虽然正确但是已经迟了,它在调用erase
之前就已经可能造成了内存的泄漏,为什么呢?
因为我们前面有看过remove
系列的相关特点,remove
在移动元素的时候,会将需要删除的元素移动到容器的末尾,而移动的过程中,会对前面需要删除的元素进行下一个不需要删除元素的赋值,赋值之后,只是改变了指针,原来的指针指向的内存就永远不会被释放了,也就造成了内存泄漏。
所以,我们需要在调用remove_if
方法的时候,需要考虑删除需要被删除的指针指向的内存。
首先我们针对析构内存写一个判断方法,该方法实现的功能是,判断是否需要析构,如果需要,则析构指针指向的内存。并将指针置为 NULL。
void clenWidget(Widget*& p)
{
if(!p->isMeasured())
{
delete p;
p = NULL;
}
}
然后通过for_each
区间函数遍历容器,对容器进行指针析构。
for_each(v.begin(), v.end(), clenWidget);
之后再针对已经析构了需要删除元素的容器进行元素的删除,现在的删除就能够正常的使用erase-remove
的用法。
v.erase(remove(v.begin(), v.end(), static_cast<Widget*>(NULL)), v.end());
但是如果容器中存的不是普通指针,而是具有引用计数功能的智能指针,也就不用考虑remove造成的困难,而可以直接使用erase-remove习惯用法。
typedef shared_ptr<Widget> WidgetSptr;
vector<WidgetSptr> v;
v.push_back(WidgetSptr(new Widget));
...
v.erase(remove_if(v.begin(), v.end(), not1(mem_fun(&Widget::isMeasured))), v.end());
主要是因为编译器会将智能指针隐式的转换为其内置指针Widget*
。容器中存放的是智能指针,但是成员函数isMeasured
必须通过内置指针才能调用。
对存储指针的容器在使用remove系列函数时要注意内存泄漏,可以使用有引用计数的智能指针代替,或者在调用remove函数之前,对需要删除的指针进行内存析构并置为0.