阿龙的学习笔记---《Effective STL》读书笔记(二)

条款12: 切勿依赖STL的线程安全性
  • 对于一个STL,最多期望:
    • 多个线程对容器的是安全的;
    • 多个线程对不同容器的写是安全的。
  • 而且上述是最多期望这个,甚至有些stl中还不支持以上的内容。
    所以程序员需要自己做同步控制:
    • 对一个容器的成员函数的调用,每次都锁住容器。
    • 对于每个返回的迭代器的生存期间内,都锁住
    • 对于算法的运行,锁住容器。
  • 锁的使用可以参考Effective C++中使用一个类来实现。

条款13:vector和string优先于动态分配的数组
  • 使用new操作符必须要注意以下三点:
    • new之后一定要delete;
    • new对应delete, new[] 对应 delete[];
    • 确保只delete一次,delete多次会带来不确定的后果。
  • 所以vector和string能够动态调整大小,并且能够自动管理内存
  • 只有一个情况特殊:在多线程环境下,string的引用计数机制可能会带来问题,而你需要做同步操作会使得效率较低。这时可以查手册,关闭string的引用计数功能,或者使用vector<char>,最后才是使用数组。

条款14:使用reserve来避免不必要的内存分配
  • STL容器能够自动增长以容纳你放进来的元素,而对于vector或string,步骤如下:
    • 分配一块当前大小2倍的大小(通常是2倍);
    • 将所有元素复制过去;
    • 析构旧的内存中的元素;
    • 释放旧内存。
  • 内存调整不仅影响效率,还会使得之前的迭代器、指针、引用都无效。所以尽早分配内存,使用reserve。
  • 其他相关函数:
    • size() 返回元素个数;
    • capacity() 告诉你分配的内存可以容纳多少元素;
    • resize(n) 强制将容器变为n个元素(非容量)的状态,多了则将尾部析构,不够则使用默认构造函数构造。
    • reserve(n) 强制容量至少是n,至少的意思是如果n小于现容量则不变。

条款15:注意string实现的多样性
  • 这个比较进阶,展示了string的多种实现方式。
  • 总结:
    • string可能引用计数也可能不是,一般可以选择关闭。
    • string对象的大小可能从1到至少7倍char*指针的大小
    • 新字符串值的建立可能需要0、1或2次动态分配

条款16: 如何将vector或string对象传给遗留的C的API
  • 比如一个老的C API:

    void doSomething(const int* pInts, size_t numInts);
    
  • 要将一个vector传入,则需要先判断v是否为空,然后取第一个元素的引用的地址:

    if (!v.empty()) {
     doSomething(&v[0], v.size());
    }
    
  • 而char* ,string有个成员函数c_str(),转换为c风格的指针。

    string s("asefa3ef");
    doSomething(s.c_str());
    

条款17:使用swap技巧去除多余的容量
  • 如下:

    • 有一个vector contestants,可能会有多余的容量。
    • 那么你建立一个临时变量,此变量以contestants为输入参数,调用拷贝构造函数,这时没有多余容量;
    • 然后再使用swap函数交换,使得contestants中没有多余容量,而临时变量中有,但临时变量会自动析构。
    class Contestant {...};
    vector<Contestant> contestants;
    
    vector<Contestant>(contestants).swap(contestants);
    
  • 需要注意:

    • 1.这个不保证没有多余容量,因为可能有些容器会有默认最小的容量。
    • 2.swap操作会让二者的迭代器、指针、引用等都改变,都被交换。

条款18: 避免使用vector<bool>
  • 由于vector<bool>会优化,一个8bit的字节会有8个bool。
  • 而对于引用等问题,采用复杂的代理对象技术。stl遗留问题。
  • 替换可以使用deque<bool>,这是真的bool对象。或者非标准的bitset。

条款19:了解等价和相等的区别
  • 相等的概念基于operator==。

  • 等价: 两个对象x和y如果在关联容器c的排序顺序中没有哪个排在
    另一个之前,那么它们关于c使用的排序顺序有等价的值。

  • 一般set以less<T>作为判断,而 less 调用 < 符号。则如果二者等价,下列表达式为true:

    !(w1 < w2) &&  !(w2 < w1)
    
  • 而再通用的情况则是用户自定义一个比较函数。比如你要让一个字符串忽略大小写放进set。

    struct CIStringCompare:	 public binary_function<string, string, bool> { // 关于这个基类的信息参见条款40
    	bool operator()(const string& lhs, const string& rhs) const	 {
    		return ciStringCompare(lhs, rhs); // 关于ciStringCompare是怎么实现的参见条款35
     	} 
    }
    
    set<string, CIStringCompare> ciss; // ciss = “case-insensitive string set”
    
  • CIStringCompare是一个仿函数类,operator()调用一个真正的函数来忽略大小写判断。

  • 则等价判别式为:(虽然这里可以用)

    !(CIStringCompare()(x, y)) &&  !(CIStringCompare()(y, x))
    
  • 则在使用成员函数操纵时会使用等价性判断,而非成员函数比如find则会使用相等性来判断:

    ciss.insert("Persephone"); // 一个新元素添加到set中
    ciss.insert("persephone"); // 没有新元素添加到set中
    if (ciss.find("persephone") != ciss.end())... // 这个测试会成功
    if (find(ciss.begin(), ciss.end(), "persephone") != ciss.end())... // 这个测试会失败
    

条款20:为元素是指针的关联容器指定比较类型
  • 元素为指针的话,默认排列会按照指针的less来进行比较,而不是指针中的value值来排序,那么就需要你去写一个取出值来判断。
    // 使用这个基类的理由参见条款40
    struct StringPtrLess: public binary_function<const string*, const string*, bool> {
     	bool operator()(const string *ps1, const string *ps2) const{
     		return *ps1 < *ps2;
     	}
    };
    
  • 然后就可以指定这个函数类的set:
    set<string*, StringPtrLess> ssp;StringPtrSet ssp; // 按照StringPtrLess定义的顺序排序
    
  • 为什么要写一个函数类而不是一个函数,是因为set不能以函数来定义。

条款21: 永远让等价性比较函数对相等的值返回false
  • 由于等价性是由以下的式子得出的结果,那么如果相等的值返回true,则整个式子返回false,这是不对的。
    !(Compare()(x, y)) &&  !(Compare()(y, x))
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值