提防在指针容器上使用类似remove的算法
假如有一个Widget类型,当它符合某一标准之后(处理完毕之后)我们准备从容器中删除,并且会每隔一段时间进行一次,类如下:
class Widget
{
public:
Widget(bool flag) : m_bIsOk(flag) {}
bool isOk()
{
return m_bIsOk;
}
private:
bool m_bIsOk;
};
我们默认构造一些值,并模拟从中删除元素:
vector<Widget*> vec = {
new Widget(false),
new Widget(false),
new Widget(true),
new Widget(false),
new Widget(true),
new Widget(false),
};
cout << "vec size:" << vec.size() << endl;
vec.erase(std::remove_if(vec.begin(), vec.end(),
std::not1(mem_fn(&Widget::isOk))), vec.end());
cout << "vec size:" << vec.size() << endl;
可以预见正常打印是size=6,和size = 4。但是这其中有一个隐含的问题,那就是我们看似正常删除了指针,但是忘记删除指针对应的内容!
所以正确的做法如下:
class Widget
{
public:
Widget(bool flag) : m_bIsOk(flag) {}
bool isOk()
{
return m_bIsOk;
}
~Widget() //析构函数的打印不可以帮助我们定位
{
cout << "Release Widget" << endl;
}
private:
bool m_bIsOk = false;
};
vec.erase(std::remove_if(vec.begin(), vec.end(), [](Widget* pWidget)
{
bool flag = pWidget->isOk();
if (flag) //释放资源
{
delete pWidget;
pWidget = nullptr;
}
return flag;
}), vec.end());
打印如下:
当然为了避免释放问题我们可以使用引用计数型智能指针,比如:
vector<std::shared_ptr<Widget>> vec
其他内容
在本节使用到的std的一些函数做一些详细的讲解:
- mem_fn
函数模板std::mem_fn为指向成员的指针生成包装对象,这些指针可以存储,复制和调用指向成员的指针。当调用一个对象时,可以使用引用和指针(包括智能指针)。
//c++ 11 例子
class Foo
{
public:
void display()
{
cout << "hello world" << endl;
}
int data = 100;
};
//c++ 14 例子
class X
{
public:
int x;
int &value() { return x; }
int &get() { return x; }
const int &get() const { return x; }
};
// c++ 11 beign
Foo f;
auto display = std::mem_fn(&Foo::display);
display(f);
auto data = std::mem_fn(&Foo::data);
cout << data(f) << endl;
// c++ 11 end
// c++14 beign
auto a = std::mem_fn(&X::value);
auto b = std::mem_fn<int&()>(&X::get);
auto c = std::mem_fn<const int&() const>(&X::get);
auto d = [](X& x) {return x.get(); };
X x = { 11 };
cout << "a:" << a(x) << endl;
cout << "b:" << b(x) << endl;
cout << "c:" << c(x) << endl;
cout << "d:" << d(x) << endl;
// c++ 14 end
打印如下:
当我们使用std算法来调用某些类的函数或者成员,都可以使用这种方式。理解起来也比较容易,使用很方便。