Effective STL 学习笔记 32 ~ 33

2023-03-03,,

Effective STL 学习笔记 32 ~ 33

<!--*/
.title { text-align: center; }
.todo { font-family: monospace; color: red; }
.done { color: green; }
.tag { background-color: #eee; font-family: monospace;
padding: 2px; font-size: 80%; font-weight: normal; }
.timestamp { color: #bebebe; }
.timestamp-kwd { color: #5f9ea0; }
.right { margin-left: auto; margin-right: 0px; text-align: right; }
.left { margin-left: 0px; margin-right: auto; text-align: left; }
.center { margin-left: auto; margin-right: auto; text-align: center; }
.underline { text-decoration: underline; }
#postamble p, #preamble p { font-size: 90%; margin: .2em; }
p.verse { margin-left: 3%; }
pre {
border: 1px solid #ccc;
box-shadow: 3px 3px 3px #eee;
padding: 8pt;
font-family: monospace;
overflow: auto;
margin: 1.2em;
}
pre.src {
position: relative;
overflow: visible;
padding-top: 1.2em;
}
pre.src:before {
display: none;
position: absolute;
background-color: white;
top: -10px;
right: 10px;
padding: 3px;
border: 1px solid black;
}
pre.src:hover:before { display: inline;}
pre.src-sh:before { content: 'sh'; }
pre.src-bash:before { content: 'sh'; }
pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
pre.src-R:before { content: 'R'; }
pre.src-perl:before { content: 'Perl'; }
pre.src-java:before { content: 'Java'; }
pre.src-sql:before { content: 'SQL'; }

table { border-collapse:collapse; }
td, th { vertical-align:top; }
th.right { text-align: center; }
th.left { text-align: center; }
th.center { text-align: center; }
td.right { text-align: right; }
td.left { text-align: left; }
td.center { text-align: center; }
dt { font-weight: bold; }
.footpara:nth-child(2) { display: inline; }
.footpara { display: block; }
.footdef { margin-bottom: 1em; }
.figure { padding: 1em; }
.figure p { text-align: center; }
.inlinetask {
padding: 10px;
border: 2px solid gray;
margin: 10px;
background: #ffffcc;
}
#org-div-home-and-up
{ text-align: right; font-size: 70%; white-space: nowrap; }
textarea { overflow-x: auto; }
.linenr { font-size: smaller }
.code-highlighted { background-color: #ffff00; }
.org-info-js_info-navigation { border-style: none; }
#org-info-js_console-label
{ font-size: 10px; font-weight: bold; white-space: nowrap; }
.org-info-js_search-highlight
{ background-color: #ffff00; color: #000000; font-weight: bold; }
/*]]>-->*/-->

div.org-src-container {
font-size: 85%;
font-family: monospace;
}
pre.src {
background-color:#f8f4d7
}
p {font-size: 15px}
li {font-size: 15px}

Table of Contents

记得 Remove 后要 Erase
remove, container, pointer

1 记得 Remove 后要 Erase

Item 9 中已经提到过,如果真的想要用 remove 从容器中删除元素,必须要在 remove 之后使用 erase
。其原因在于: remove 接受的参数是通用的迭代器,而不是容器,它不知道容器的任何信息,也就无法从容器中删除元素: remove doesn't really remove anything, because it can't.

remove 只保证在其返回的迭代器之前的元素为有效数据(符合条件的数据),但它既无法删除被 remove
的数据,也不能保证返回的迭代器之后的数据位无效数据(不符合条件的数据)。这一点与某些容器内置的 remove member function 不一样:这些 member function 会真正的把数据删除掉:

// For vector, we have to call erase after remove.
vector<int> v;
// ....
vector<int>::iterator endPos = remove(v.begin(), v.end(), 99); // Remove 99 from vector, returns last valid position.
v.erase(posEnd, v.end()); // Erase all elements after posEnd. // For list, the member function remove is enough:
list<int> l;
//...
l.remove(99); // this will erase all 99 in the list.

2 remove, container, pointer

Item 6 中提到,在容器中放置对象指针容易混乱,这里如果对存放指针的容器调用 remove-erase idom
的话,会发生什么事情?

 1: class Widget
2: {
3: public:
4: Widget();
5: virtual ~Widget();
6: bool IsCertified();
7: };
8:
9: vector<Widget*> v;
10:
11: // push lots of widget pointers to v.
12:
13: v.erase(remove(v.begin(), v.end(), not1(mem_fun(&Widget::IsCertified))), v.end());

内存泄露。

那么将第 13 行换成下面的表达式呢?

14: vector<Widget*> endPos = remove(v.begin(), v.end(), not(mem_fun(&Widget::IsCertified)));
15: for_each(endPos, v.end(), [](Widget* pw){if(pw) delete pw;});
16: v.erase(endPos, v.end());

第 14 行将所有的 endPos 之后的指针先释放,然后再去 erase。前面刚刚提过, remove 不会保证返回的迭代器之后的元素都为无效值,第 14 行有可能会将本该保留的对象给释放掉,还有可能会将该释放的不释放。结果可能会 Crash 或者内存泄露。

我们应该保证在 remove 过程中,一旦发现不合要求的数据,马上将其删除,例如下面的例子:

#include <vector>
#include <iterator>
#include <algorithm>
#include <iostream>
#include <stdio.h> using namespace std; class Widget
{
public:
Widget(int i);
virtual ~Widget();
bool IsCertified();
void Show();
private:
int m_i;
}; /* See description in header file. */
Widget::Widget(int i)
: m_i(i)
{
} /* See description in header file. */
Widget::~Widget()
{
printf ("Deleting Obj: %p, value: %d\n", this, m_i);
} /* See description in header file. */
bool Widget::IsCertified()
{
return m_i % 2 == 0;
} /* See description in header file. */
void Widget::Show()
{
printf ("\tObj: %p, value: %d\n", this, m_i);
} bool DeleteIfUncitified(Widget* p)
{
if (p && !p->IsCertified())
{
delete p;
return true;
}
return false;
} int main(int argc, char *argv[])
{
vector<Widget*> v;
for (int i = 0; i < 20; ++i)
{
Widget* p = new Widget(i);
v.push_back(p);
} random_shuffle(v.begin(), v.end()); printf ("Before remove, size: %lu, contents:\n", v.size());
for_each(v.begin(), v.end(),
[](Widget* p){
if (p) p->Show();
else printf("Obj is Null");});
printf ("\nNow removing...\n");
v.erase(remove_if(v.begin(), v.end(), DeleteIfUncitified), v.end());
printf ("\nAfter remove, size: %lu, contents:\n", v.size());
for_each(v.begin(), v.end(),
[](Widget* p){
if (p) p->Show();
else printf("Obj is Null");}); return 0;
}

其输出为:

Welcome to the Emacs shell

~/tmp $ ./test
Before remove, size: 20, contents:
Obj: 0x7f8b31c038d0, value: 19
Obj: 0x7f8b31c03870, value: 3
Obj: 0x7f8b31c039e0, value: 14
Obj: 0x7f8b31c039f0, value: 15
Obj: 0x7f8b31c03890, value: 10
Obj: 0x7f8b31c03850, value: 2
Obj: 0x7f8b31c03900, value: 6
Obj: 0x7f8b31c038b0, value: 17
Obj: 0x7f8b31c03920, value: 8
Obj: 0x7f8b31c038a0, value: 4
Obj: 0x7f8b31c039c0, value: 12
Obj: 0x7f8b31c03910, value: 7
Obj: 0x7f8b31c038f0, value: 5
Obj: 0x7f8b31c039b0, value: 11
Obj: 0x7f8b31c03860, value: 1
Obj: 0x7f8b31c03880, value: 9
Obj: 0x7f8b31c03840, value: 0
Obj: 0x7f8b31c03a00, value: 16
Obj: 0x7f8b31c039d0, value: 13
Obj: 0x7f8b31c038c0, value: 18 Now removing...
Deleting Obj: 0x7f8b31c038d0, value: 19
Deleting Obj: 0x7f8b31c03870, value: 3
Deleting Obj: 0x7f8b31c039f0, value: 15
Deleting Obj: 0x7f8b31c038b0, value: 17
Deleting Obj: 0x7f8b31c03910, value: 7
Deleting Obj: 0x7f8b31c038f0, value: 5
Deleting Obj: 0x7f8b31c039b0, value: 11
Deleting Obj: 0x7f8b31c03860, value: 1
Deleting Obj: 0x7f8b31c03880, value: 9
Deleting Obj: 0x7f8b31c039d0, value: 13 After remove, size: 10, contents:
Obj: 0x7f8b31c039e0, value: 14
Obj: 0x7f8b31c03890, value: 10
Obj: 0x7f8b31c03850, value: 2
Obj: 0x7f8b31c03900, value: 6
Obj: 0x7f8b31c03920, value: 8
Obj: 0x7f8b31c038a0, value: 4
Obj: 0x7f8b31c039c0, value: 12
Obj: 0x7f8b31c03840, value: 0
Obj: 0x7f8b31c03a00, value: 16
Obj: 0x7f8b31c038c0, value: 18
~/tmp $

(转载请注明出处,使用许可:署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议 。)

Effective STL 学习笔记 32 ~ 33的相关教程结束。

《Effective STL 学习笔记 32 ~ 33.doc》

下载本文的Word格式文档,以方便收藏与打印。