zoukankan      html  css  js  c++  java
  • STL笔记(3) Effective STL

    Effective STL

     

    1: 慎重选择容器类型

     

    2: 不要试图编写独立于容器类型的代码

     

    3: 确保容器中的对象copy正确且搞笑

    存在继承关系时的copy会导致分割, 可以存储对象指针.

     

    4: 调用empty而不是检查size()是否为0

     

    empty对所有标准容器是常数时间操作.

    size()返回容器元素个数, 耗费线性时间.capacity()返回能够存储的总数, 对于vector(type) c, capacity0, 不能赋值, 必须用reserve分配空间.

     

    5: 区间成员函数优先于与之对应的单元素成员函数.

    避免写显示循环, 这样减少对象拷贝, 减少代码.

     1 vector<int> v1, v2;
    2
    3 //...
    4

    5 v1.clear();
    6
    7 for(vector<int>::const_iterator ci = v2.begin(); ci != v2.end(); ++ci)
    8
    9 {
    10
    11 v1.push_back(*ci);
    12
    13 }
    14
    15 相当于
    16
    17 v1.assign(v2.begin(), v2.end());
    18
    19



    而算法

    copy(v2.begin(), v2.end(), back_inserter(v1));

    中尽管没有显示循环, 但是copy算法中可能有. 可以改为:

    v1.insert(v1.end(), v2.begin(), v2.end());

     

    6: C++编译器最烦人的分析机制

    C++尽可能的解释为函数声明;

    1 ifstream dataFile("xx.txt");
    2 list<int>data(istream_iterator<int>(dataFile),
    3 istream_iterator<int>()); //被认为是函数声明, 而不是够找一个对象.



    最好改成:

    1 ifstream dataFile("xx.txt");
    2
    3 istream_iterator<int> data1(dataFile);
    4
    5 istream_iterator<int> data2;
    6
    7 list<int>data(data1, d


    ata2);

     

    7: 如果容器中包含了通脱new操作创建的指针, 在容器对象析够前将指针delete

    容器在析构时会析构容器元素, 但是如果是指针容器, delete操作, 需手动delete

    或者可以存储只能指针,

     

    8: 切勿创建包含auto_ptr的容器.

     

    iterator_traits<pointobj>::value_typepointobj所指向的类型, 如果pointobjvector<int>::iterator, iterator_traits<pointobj>::value_typeint

     

    9: 慎重选择删除元素的方法

     

    erase-remove方法.

    set, multiset, map, multimupremove方法.

    erase,所有删除元素之后的指向元素的迭代器会无效.

     

    10. 了解分配自的约定和限定

     

    11. 理解自定义分配子的合理用法

     

    12. 切勿对STL容器的线程安全性有不切实际的依赖

     

    13. vectorstring优先于动态分配的数组

     

    string使用了引用计数计数, 在多线程中会比较麻烦, 需要同步控制, 使用vector<char>替代string, vector不允许使用引用计数.

     

    14. 使用reserve来避免不必要的重新分配

     

    15: 注意string实现的多样性

     

    16: 了解如何把vectorstring数据传给旧的api

     

    vector使用&v[0], 或者&*v.begin(), 注意,vector可能为空, 则产生野指针.

    string使用s.c_str();

     

    17: 使用swap技巧除去多余的容量.

     

    vector<int> v;

    //...

    vector<int>(v).swap(v); // vector<int>(v)创建临时变量, vcopy, vectorcopy构造函数只分配所需的内存. swap止呕, v去除了不需要的空间.

     

    18: 避免使用vector<bool>

    vector<bool>并不真正存储bool, 为了节省空间, vector中的bool只占一个二进制位.

    vector必须支持operator[],vector<bool>不允许. &v[0]也不允许.

    deque<bool> OK

     

    19: 理解equalityequivalence的区别.

     

    相等equality是以operator==, 等价是以A不大于BB也不大于A.

     

    20: 为包含指针的关联容器指定比较类型

     

    set默认的比较函数是less.

    set<string *> ssp实际为set<string*, kess<strubg*>>ssp;

    所以需要编写string *的比较函数

     1 struct stringPtrLess:
    2
    3 public binary_function<const string*, cosnt string*, bool>{
    4
    5 bool operator()(const string *ps1, const string *ps2)const
    6
    7 {
    8
    9 return *ps1 > *ps2;
    10
    11 }
    12
    13 }
    14
    15 set<string *, stringPtrLess> ssp;



    21: 总是让比较函数在等值情况下返回false

     

    22: 切勿直接修改setmultiset中的键

     

    map, multimap中键也不可更改.元素类型为pair<const k, v>

     

    23: 考虑用排序的vector替代关联容器

     

    标准关联容器通常被实现为平衡的二叉树查找.

    sort算法排序vector.

     

    24: 当效率至关重要时, 请在map::operator[]map::insert之间谨慎抉择

     

    如果key不存在, map::operator[]相当于先构造再赋值

    1 map<int, string> m;
    2
    3 m[1] = "123";



    相当于

     1 pair<map<int, string>::iterator, bool> result = m.insert(map<int, string>::value_type(1, string()));
    2
    3 result.first->second = "123";
    4
    5
    6
    7 insert:
    8
    9 map<int, string> m;
    10
    11 m.insert(map<int, string>::value_type( 1, "123"));



    如果元素已经存在, 优先选择operator[], 代码简洁.

     

    25: 熟悉非标准的哈希容器

     

    26: iterator优先于const_iterator, reverse_iterator, const_reverse_iterator

     

     

    iteratorconst_iterator不是同一类型, 不能用const_cast转换.

     

    27: 使用distanceadvance将容器的const_iterator转换沉iterator

    1 deque<int> d;
    2
    3 deque<int>::const_itertaor ci;
    4
    5 deque<int>::iterator i(d.begin());
    6
    7 advance(i, distance< deque<int>::const_itertaor >(i, ci); /*必须将i转换成deque<int>::const_itertaor, 否则distance模板无法编译.*/


     

    distance两个迭代器之间的距离, advance将迭代器移动指定距离.

     

    28: 正确理解有reverse_iteratorbase()成员函数所产生的iterator的用法

     

     

    相差一个位置

    但是不可用v.erase(--ri.base()); C/C++规定了从函数返回的指针不能被修改.

    所以改为v.erase((++ri).base());

     

     

    29: 对于逐个字符的输入请考虑使用istreambuf_iterator

     

     

    30:确保目标区间足够大

     

    确保vector中元素存在, 如果不存在, 调用resize, 或者使用back_iterator.

    reserve只是增加容器的容量, 而容器size并未改变.

     

    31: 了解各种与排序有关的选择

     

    sort 完全排序

    stable_sort 稳定排序, 如果AB相等且排序前AB, 则排序后A仍然在B

    partial_sort 之排序部分,sortEnd为排序的最后一位的下一个.

     

     1 template<class RandomAccessIterator>
    2
    3 void partial_sort(
    4
    5 RandomAccessIterator first,
    6
    7 RandomAccessIterator sortEnd,
    8
    9 RandomAccessIterator last
    10
    11 );
    12
    13 template<class RandomAccessIterator, class BinaryPredicate>
    14
    15 void partial_sort(
    16
    17 RandomAccessIterator first,
    18
    19 RandomAccessIterator sortEnd,
    20
    21 RandomAccessIterator last
    22
    23 BinaryPredicate comp
    24
    25 );
    26
    27
    28
    29 nth_element 与partial_sort不同之处在于只是将某些元素移至到前面,但并未排序.
    30
    31 template<class RandomAccessIterator>
    32
    33 void nth_element(
    34
    35 RandomAccessIterator _First,
    36
    37 RandomAccessIterator _Nth,
    38
    39 RandomAccessIterator _Last
    40
    41 );
    42
    43 template<class RandomAccessIterator, class BinaryPredicate>
    44
    45 void nth_element(
    46
    47 RandomAccessIterator _First,
    48
    49 RandomAccessIterator _Nth, //排序的最后一个元素
    50

    51 RandomAccessIterator _Last,
    52
    53 BinaryPredicate _Comp
    54
    55 );



     

    partition 将满足条件的元素移至前面, 返回迭代器指向第一个不满足条件的元素

     

    stable_partition

     

    32: 如果确实需要删除元素, 需要在remove这类算法后调用容器的erase

     

    remove, remove_if, unique等算法不是真正的删除, 算法不会改变容器元素.需要调用

    v.erase(remove(v.begin(), v.end(), 22));

    list::removelist_unique会真正删除元素.

     

     

    33: 对包含指针的容器使用remove这一类算法时要特别小心

     

    会将某些指针覆盖无法delete

    应该先用for_each将不符合的元素指针delete再置为0, 之后removeerase

     

    34: 了解哪些算法要求使用排序的区间作为参数

     

    35: 通过mismatchlexicographical_compare实现简单的忽略大小写的字符串比较

     

    mismatch

    mismatch(s1.begin(), s1.end(), s2.begin(), not2(ptr_fun(charCompare))); 返回pair, firsts1的第一个不匹配的迭代器, seconds2的第一个不匹配的迭代器, 如果都匹配, 则返回pair(s1.end(), s2.begin()+s1.end()-s1.begin());

    lexicographical_compare接受判别式判断两个区间大小, 直到判别式不为true, 如果第一个却见结束了, 返回true

     1 bool charLess(char c1, char c2)
    2
    3 {
    4
    5 return tolower(static_cast<unsigned char> (c1)<tolower(static_cast<unsigned char> (c2);
    6
    7 }
    8
    9 bool stingCompare(const string &s1, const string &s2)
    10
    11 {
    12
    13 return lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), charLess);
    14
    15 }
    16
    17



    36: 理解copy_if算法的正确实现

     

    STL中没有copy_if, 利用remove_copy_if

     

    37: 使用accumulatefor_each进行区间统计

     

    38: 遵循按值传递的原则来设计函数子类

     

    39: 确保判别式是纯函数

     

    判别式是返回bool类型的函数

     

    纯函数是返回值仅和参数相关的函数.只有参数发生变化,返回值才变化.

    判别式类是函数子类, operator()为判别式.

     

    40: 若一个类是函数子, 则应使它可配接

     1 bool charLess(const char c)
    2
    3 {
    4
    5 return c>40;
    6
    7 }
    8
    9 int _tmain(int argc, _TCHAR* argv[])
    10
    11 {
    12
    13 find_if(s.begin(), s.end(), not1(charLess)); /*无法编译,charLess缺少not所需的类型, ptr_fun完成了类型定义的工作*/
    14
    15 find_if(s.begin(), s.end(), not1(ptr_fun(charLess)));
    16
    17 }



    最简单的方法是让函数子从特定的基类继承, 如果函数子的operator()只有一个实参, 则继承std::unary_function, 如果有两个实参, 继承std::binary_function, 以上基类都为模板, 继承是需指定实参和返回类型.

    struct stringCompare:public std::binary_function<string, string, bool>

    /*注意, 去掉const和&, 但是如果类型为指针, 则不能去掉*/

    {

    public:

    bool operator()(const string& s1, const string& s2) const

    {

    return s1<s2;

    }

    };

    int _tmain(int argc, _TCHAR* argv[])

    {

    vector<string> v;

    //...

    find_if(v.begin(), v.end(), bind2nd(stringCompare(), "123"));

    }



    41: 理解ptr_fun, mem_fun, mem_fun_ref的来由

     

    mem_fun 参数为成员函数指针, 并返回mem_fun_t类型对象, mem_fun_toperator()函数中调用了通过参数传递进来的对象上的成员函数

    mem_fun_refmem_fun, 区别为一个是对象引用, 一个是函数指针.

     1 class Widget
    2
    3 {
    4
    5 public:
    6
    7 void test() //paramter: this
    8

    9 {
    10
    11 }
    12
    13 };
    14
    15 vector<Widget> v;
    16
    17 //...
    18

    19 for_each(v.begin(), v.end(), mem_fun_ref(&Widget::test));
    20
    21
    22
    23 vector<Widget *> pv;
    24
    25 //...
    26

    27 for_each(pv.begin(), pv.end(), mem_fun(&Widget::test));
    28
    29



    42: 确保less<T>operator<具有相同的意义

     

    43: 算法调用优先于手写的循环

     

    44: 容奇的成员函数优先于同名的算法

     

    45: 正确区分count, find, binary_seach, lower_bound, upper_bound, equal_range

     

    count, find使用相等性搜索, binary_seach, lower_bound, upper_bound, equal_range使用了等价性.

     

    46: 考虑使用函数对象而不是函数作为STL算法的参数

     

    函数作为参数时, 会产生函数调用, 且函数指针抑制了inline机制.

     

    47: 避免产生write_only代码

     

    48: 总是包含正确的头文件

     

    49: 学会分析与STL相关的编译器诊断信息

     

    50: 熟悉与STL相关的web站点

  • 相关阅读:
    csp2020游记
    agc006_f Blackout
    CF1368G Shifting Dominoes
    AtCoder Grand Contest 009 简要题解
    Codeforces Round #666 (Div. 1)
    CSP 2019 树的重心
    Luogu-P4859 已经没什么好害怕的了
    2020.9.17 校内测试
    CF379F New Year Tree
    图论(小结论)
  • 原文地址:https://www.cnblogs.com/zengyou/p/2195602.html
Copyright © 2011-2022 走看看