《Effective STL》读书笔记之容器6-12

参考链接:

https://www.cnblogs.com/virusolf/p/4903717.html

http://dongxicheng.org/cpp/effective-stl-part1/

第一章 容器

第六条:警惕C++最令人恼怒的解析

C++会尽可能的将一条语句解释为函数声明。
下列语句都声明了一个函数返回值为int类型的函数f,其参数是double类型。
int f(double(d));
int f(double d);
int f(double);
下列语句都声明了一个返回值为int类型的函数g,它的参数是返回值为double类型且无参的函数指针。
int g(double(*pf)());
int g(double pf());
int g(double ()); //注意与int g(double (f))的区别
对于如下语句,编译器会做出这样的解释:声明了一个返回值为list的函数data,该函数有两个参数,一个是istream_iterator类型的变量,另一个是返回值为istream_iterator类型的无参函数指针。
ifstream dataFile("ints.dat");
list data(istream_iterator(dataFile),istream_iterator());
如果希望构造一个list类型的变量data,最好的方式是使用命名的迭代器。尽管这与通常的STL风格相违背,但是消除了编译器的二义性而且增强了程序的可读性。
ifstream dataFile("ints.dat");
istream_iterator dataBegin(dataFile);
istream_iterator dataEnd;
list data(dataBegin,dataEnd); 

第七条:如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉

STL容器在析构之前,会将其所包含的对象进行析构。
class widget
{
    ...
};

doSth()
{
    widget w; //一次构造函数
    vector v;
    v.push_back(w); //一次拷贝构造函数
 } // 两次析构函数
但如果容器中包含的是指针的话,一旦没有特别将指针delete掉将会发生内存泄漏。
class widget
{
     ...
};

doSth()
{
    widget* w = new widget();
    vector v;
    v.push_back(w); 
} // memory leak!!!
最为方便并且能够保证异常安全的做法是将容器所保存的对象定义为 带有引用计数的智能指针
class widget
{
    ...
};

doSth()
{
    shared_ptr w(new widget()); //构造函数一次
    vector> v;
    v.push_back(w); 
} //析构函数一次 没有内存泄漏

第八条:切勿创建包含auto_ptr对象的容器

由于auto_ptr对于其"裸指针"必须具有独占性,当将一个auto_ptr的指针赋给另一个auto_ptr时,其值将被 置空NULL
auto_ptr p1(new int(1)); // p1 = 1
auto_ptr p2(new int(2)); // p2 = 2
 
p2 = p1; // p2 = 1  p1 = emtpy;
第三条提到STL容器中的插入对象和读取对象,使用的都是对象的拷贝,并且基于STL容器的算法也通常需要进行对象的copy,所以,创建包含auto_ptr的容器是不明智的。

第九条:慎重选择删除元素的方法

(1)假定你有一个标准STL容器c,容纳int
Container c;
而你想把c中所有值为1963的对象都去,则不同的容器类型采用的方法不同,没有一种是通用的。
[1] 如果采用连续内存容器(vector、queue和string),最好的方法是erase-remove惯用法:
c.erase(remove(c.begin(), c.end(), 1963), // 当c是vector、string
 
c.end()); // 或deque时,erase-remove惯用法是去除特定值的元素的最佳方法
[2] 对于list,最有效的方法是直接使用remove函数:
c.remove(1963);
[3] 对于关联容器,解决问题的适当方法是调用erase:
c.erase(1963); // 当c是标准关联容器时,erase成员函数是去除特定值的元素的最佳方法
(2)让我们换一下问题:不是从c中除去每个有特定值的元素,而是消除下面判断式返回真的每个对象:
bool badValue(int x); // 返回x是否是“bad”
[1] 对于序列容器(vector、list、deque和string),只需要将remove换成remove_if即可:
c.erase(remove_if(c.begin(), c.end(), badValue), // 当c是vector、string
 
c.end()); // 或deque时这是去掉badValue返回真的对象的最佳方法
 
c.remove_if(badValue); // 当c是list时这是去掉badValue返回真的对象的最佳方法
[2] 对于关联容器,有两种方法处理该问题,一个更容易编码,另一个更高效。“更容
易但效率较低”的解决方案用remove_copy_if把我们需要的值拷贝到一个新容器中,然后把原容器的内容和新的交换:
AssocContainer c; // c现在是一种标准关联容器
 
AssocContainer goodValues; // 用于容纳不删除的值的临时容器
 
remove_copy_if(c.begin(), c.end(), // 从c拷贝不删除
 
inserter(goodValues, // 的值到
 
goodValues.end()), // goodValues
 
badValue);
 
c.swap(goodValues); // 交换c和goodValues的内容
“更高效”的解决方案是直接从原容器删除元素。不过,因为关联容器没有提供类似remove_if的成员函数,所以我们必须写一个循环来迭代c中的元素,和原来一样删除元素:
AssocContainer c;
 
...
 
for (AssocContainer::iterator i = c.begin(); // for循环的第三部分
 
i != c.end(); // 是空的;i现在在下面
 
/*nothing*/ ){ // 自增
 
if (badValue(*i)) c.erase(i++); // 对于坏的值,把当前的
 
else ++i; // i传给erase,然后
 
} // 作为副作用增加i;对于好的值,只增加i
(3)进一步丰富该问题:不仅删除badValue返回真的每个元素,而且每当一个元素被删掉时,我们也想把一条消息写到日志文件中。

[1] 对于关联容器,只需要对我们刚才开发的循环做一个微不足道的修改就行了:
ofstream logFile; // 要写入的日志文件
 
AssocContainer c;
 
...
 
for (AssocContainer::iterator i = c.begin(); // 循环条件和前面一样
 
i !=c.end();){
 
if (badValue(*i)){
 
logFile << "Erasing " << *i <<'\n'; // 写日志文件
 
c.erase(i++); // 删除元素
 
}
 
else ++i;
 
}
[2] 对于vector、string、list和deque,必须利用erase的返回值。那个返回值正是我们需要的:一旦删除完成,它就是指向紧接在被删元素之后的元素的有效迭代器。换句话说,我们这么写:
for (SeqContainer::iterator i = c.begin();
 
i != c.end();){
 
if (badValue(*i)){
 
logFile << "Erasing " << *i << '\n';
 
i = c.erase(i); // 通过把erase的返回值赋给i来保持i有效
 
}
 
else
 
++i;
 
}


第十条:注意分配器的协定和约束

如果需要编写自定义的分配子,有以下几点需要注意:
  • 当分配子是一个模板,模板参数T代表你为其分配内存的对象的类型
  • 提供类型定义pointer和reference,始终让pointer为T*而reference为T&
  • 不要让分配子拥有随对象而不同的状态,通常,分配子不应该有非静态数据成员
  • 传递给allocator的是要创建元素的个数而不是申请的字节数,该函数返回T*,尽管此时还没有T对象构造出来
  • 必须提供rebind模板,因为标准容器依赖于该模板

第十一条:理解自定义分配子的合理用法

TODO


第十二条:切勿对STL容器的线程安全性有不切实际的依赖

  • 对于STL容器的多线程读是安全的
  • 对于多个不同的STL容器
采用面向对象的方式对STL容器进行加锁和解锁。
template
class lock
{
public:

    Lock(const Container& container):c(container)
    {
        getMutexFor(c);
    }

    ~Lock()
    {
        releaseMutex(c);
    }

private:

    Container& c;
},


vector v;
...

{
    Lock> lock(v); //构造lock,加锁v
    doSthSync(v); //对v进行多线程的操作
} //析构lock,解锁v



你可能感兴趣的:(Effective,STL读书笔记,Cpp,STL,容器)