美文网首页
《C++ Primer》第||部分读书笔记(第8~12章)

《C++ Primer》第||部分读书笔记(第8~12章)

作者: 十月石榴2013 | 来源:发表于2017-12-18 20:46 被阅读19次
    C++ Primer.png

    第八章

    ioseam
    iofsteam
    文件的操作:https://www.cnblogs.com/likebeta/archive/2012/06/16/2551662.html
    写程序流程要求:
    ① 创建输入输出流,判断文件是否打开成功;
    ② 如果文件打开成功,则处理。
    iostringsteam

    第九章

    1. 顺序容器

    容器类型 概述
    vector 可变大小数组,支持快速访问。在尾部之外的位置插入删除数据元素可能很慢
    deque 双端队列。支持快速随机访问,在头部位置插入/删除速度很快
    list 双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除操作速度都很快
    forward_list 单向链表,只支持单项顺序访问。在链表任何位置进行插入/删除操作速度都很快
    array 固定大小数组。支持快速随机访问,不能添加或删除元素
    string 与vector相似的容器,但专门用于保存字符。随机访问快。在尾部插入/删除速度快

    2. 迭代器
    如果不确定应该使用哪种容器,你们可以在程序中只是用vector和list的公共操作:使用迭代器,不使用下标操作,避免随机访问。这样,在必要的时候使用vector或list都很方便。
    一个迭代器范围(iterator range)由一对迭代器表示,两个迭代器分别指向同一个容器中的元素或者是尾元素之后的位置
    · 如果begin与end不等,则范围至少包含一个元素,且begin指向范围中的第一个元素。
    · 我们可以对begin递增若干次,使得begin==end。so:

    while (begin != end )
    {
        *begin = val;
        ++begin;
    }
    

    当我们向容器中添加或删除元素时,迭代器可能失效。使用失效的迭代器、指针或引用的严重的运行时错误。
    因此必须保证每次改变容器的操作之后都正确地重定位迭代器。
    例子:

    // 傻瓜循环,删除偶数元素,复制每个奇数元素
    vector<int> vi = {0,1,2,3,4,5,6,7,8,9 };
    auto iter = vi.begin();
    while (iter != vi.end()){    // 不要保存end返回的迭代器,因为插入或删除元素后,end中的迭代器失效了
        if (*inter % 2){
            iter = vi.insert( iter, *iter );
            iter += 2;
        }
        else 
            iter = vi.erase( iter );
            // 不应移动迭代器,iter指向我们删除的元素之后的元素
    }
    

    3. vector 对象是如何增长的
    vector对象要求元素连续存储
    标准库实现者采用了可以减少容器空间重新分配的次数的策略。不得不获取新的空间时,vector和string的实现通常会分配新的空间需求更大的内存空间。容器预留这些空间作为备用,可用来保存更多的新元素。这样,就不需要每次添加新元素都重新分配空间了。
    c.capacity() 不重新分配内存空间的话,c可以保存多少元素;
    c.reserve(n) 分配至少能容纳n个空间的内存元素。
    vector的实现采用的策略是每次需要分配新内存时,将当前容量翻倍。但也可以选择自己的内存分配策略。但是必须遵守的一条原则是:只有当迫不得已时,才可以分配新的内存空间。

    4. string的其他操作
    substr

    string s("hello world");
    string s2 = s.substr(0, 5);         // s2 = hello
    string s3 = s.substr(6);            // s3 = world
    string s4 = s.substr(6, 11);         // s2 = world
    string s4 = s.substr(12);         // 抛出一个out_of_range异常
    

    string的搜索操作:
    str.find("hello"),返回子字符串的第一个字母的位置。
    还用很多其他搜索方法。

    5. 容器适配器
    除了顺序容器之外,标准库还定义了三个顺序容器适配器:stack、queue和priority_queque。
    适配器(adaptor)是标准库中的一个通用概念。容器、迭代器和函数都有适配器。本质上,一个适配器是一种机制,能使某种事物的行为看上去像是另外一类事物一样。
    对于给定的适配器,可以使用哪些容器是有限制的。

    —————————二刷分割线———————————
    容器类型成员
    为了使用这些类型,必须显式使用其类型名,例子:

    list<string>::literator iter    // 迭代器iter是string的list的迭代器
    

    容器操作
    —————————二刷分割线———————————

    第十章

    1. 泛型算法概念
    容器只定义了很少的操作,标准库定义了一组泛形算法(generic algorithm)。定义在头文件algorithm中。
    一般情况下,这些算法不直接操作容器,而是遍历有两个迭代器指定的一个元素范围。
    例子:find

    int val = 42;  // 我们将要查找的值
    auto result = find(vec.cbegin(), vec.cend(), val);
    cout << "The Value  " << val
            << (result == vec.cend() )? "is not present " : "is present" << endl;
    

    迭代器令算法不依赖与容器:
    泛型算法本身不会执行容器的操作,他们只会运行在迭代器上,执行迭代器的操作。泛型算法可能改变容器中保存的值,也可能在容器内移动元素,但永远不会直接添加或删除元素。

    2. 只读算法
    3. 写容器的算法
    算法不检查写操作。例子:fill_n

    vector<int> vec;    // 空vector
    fill_n(vec.bgin(), vec.size(), 0);
    

    函数fill_n默认写入指定元素是安全的。一个初学者非常容易犯的错误就是在一个空容器上调用fill_n(或类似的写元素的算法),此行为的后果是位未定义的。

    4. lambda函数
    匿名函数,只是用一次所以连名字都没要必要费心取的函数,不取名反而更增加整个程序的可读性。
    参考资料:
    https://www.zhihu.com/question/20125256
    对于只在一两个地方使用的简单操作,lambda表达式是最有用的。如果我们需要在很多地方用相同的操作,通常应该定义一个函数,而不是多次编写相同的lambda表达式。
    关于lambda函数的捕获值的资料:http://blog.csdn.net/zh379835552/article/details/19542181
    例子可见二刷for_each部分。

    5. bind 函数适配器
    当只能传入一个参数的时候,通过bind可以传入多个参数。例子:

    // 按单词长度由短至长排序
    sort(words.begin(), words.end(), isShorter);
    // 按单词长度由长至短排序
    sort(words.begin(), words.end(), bind(isShorter, _2, _1);
    

    —————————二刷分割线———————————
    只读算法
    ① accumulate,例子:

    int sum = accumulate ( vec.cbegin(), vec.cend(), 0);  // 对vec中元素求和,和的初始值为0
    

    ② equal,可用于比较两个不同类型的容器中的元素,元素类型不同也可以,只要能用==来比较两个元素即可。
    写容器的算法
    ① fill,填充元素。
    ② back_inserter
    ③ copy,拷贝算法。例子:

    int a1[] = {0,1,2,3,4,5,6,7,8,9};
    int a2[sizeof(a1) / sizeof(*a1)];    // a2的大小和a1一样
    // ret指向拷贝到a2的尾元素之后的位置
    auto ret = copy(begin(a1), end(a1), a2);  // 把a1的内容拷贝给a2
    

    ④ replace算法,例子:

    replaice(ilist.begin(), ilist.end(), 0, 42)  // 将所有值为0的替换成值为42
    

    重排容器元素的算法
    ① sort 排序
    ② unique 不重复元素排序(返回指向不重复排序尾部的指针)
    ③ stable_sort 对某一系列排序,并保证相等的元素的顺序还是原来的顺序,例子+详情:
    http://blog.csdn.net/earbao/article/details/54911878
    可用于双重或多重标准的排序。
    删除操作
    erase,例子:

    auto end_unique = unique(words.begin(), words.end());
    words.erase(end_unique, words.end());
    

    查找算法
    ① find
    ② find_if,在两个迭代器(函数前两个入参)之间的范围内,查找符合条件(第三个入参)的值,返回一个迭代器。例子:

    auto wc = find_if (word.begin(), word.end(), 
        [sz](const string &a)
            { return a.size() >= sz; });
    

    其他
    for_each算法,对两个迭代器(函数的前两个入参)之间的每个元素进行某操作(第三个入参)例子:

    // 例1:打印出每个单词
    for_each(words.begin(), words.end(),
            [](const string &s)(const string &s){cout << s << " ";})
    
    // 例2:lamda函数两个入参,打印字符到os
    for_each(words.begin(), words.end(),
            [&os, c](const string &s){ os << s << c; });
    // os是引用捕获,所以可以改变os的值,而c是值捕获。
    

    —————————二刷分割线———————————

    第十一章

    类型 说明 是否有序保存 其他
    map 关联数组:保存键值对(key-value) 按关键字有序保存元素 例子:字典
    set 关键字即值,即只保存关键字的容器 有序保存
    multimap 关键字可重复出现的map 有序保存
    multimap 关键字可重复出现的map 有序保存
    unordered_map 用哈希函数组织的map 无序集合
    unordered_set 用哈希函数组织的set 无序集合
    unordered_multimap 哈希组织的map,关键词可重复出现 无序集合
    unordered_multiset 哈希函数的set,关键词可重复出现 无序集合

    关联容器
    关联容器支持高校的关键字查找和访问。两个主要的挂念容器是mapset
    标准库提供8个关联容器如表所示。
    map类型通畅被称为“关联数组”。关联数组与正常数组类似,不同的是其下标不必是整数。
    与之相对,set就是关键字的简单集合。当只想知道一个值是否存在时,set是最有用的。例子:

    // 例子:用set保存想忽略的单词,只不在集合中出现的单词统计出现次数
    map<string, site_t> word_count; 
    set<string> exclude = {"the","a","an","The","A","an"};
    string word;
    while(cin>>word)
        if(exclude.fine(word) == exclude.end)
        {
            ++word_count[word];
        }
    

    pair类型
    pair是map的元素。
    管理桶
    无序容器在存储上为一组桶,每个桶存储零个或多个元素。

    第十二章

    静态内存用来保存局部static对象、类static成员,以及任意定义在任何函数之外的变量。占内存用来保存定义在函数内的非static对象。
    分配在静态或栈内存或占内存中的对象由编辑器自动创建和销毁。
    除了静态内存和占内存,每个程序还拥有一个内存池。这部分内存被称为自由空间或堆。程序用堆来存储动态分配对象。

    动态内存
    new:在动态内存中为对象分配空间并返回一个指向该对象的指针。
    delete:接受一个动态对象的指针,销毁该对象,并释放预支关联的内存。
    动态内存的问题:

    1. 忘记释放内存——内存泄漏。
    2. 尚有指针引用内存时释放了它——产生非法引用内存的指针。

    智能指针:和普通指针的区别是负责自动释放所指向的对象。
    shared_ptr 允许多个指针指向一个对象;unique_ptr“独占”所指的对象。此外,还有weak_ptr一种弱引用。

    allocator类
    定义在头文件memory中,帮助我们将内存分配和对象构造分离开来。它提供一种类型感知的内存分配方法,它分配的内存是原始的、为构造的。

    其他备忘

    单冒号
    可以用于取出vector中的每一项。例子:

    for ( const auto &entry : people)  // 对people中的每一项
    {
        // ......
        for ( const auto &nums : entry.phones)    // 对entry中的每个数
        {
            // ......
        }
    }
    

    a2的大小和a1一样

    int a1[] = {0,1,2,3,4,5,6,7,8,9};
    int a2[sizeof(a1) / sizeof(*a1)];    
    

    相关文章

      网友评论

          本文标题:《C++ Primer》第||部分读书笔记(第8~12章)

          本文链接:https://www.haomeiwen.com/subject/xounixtx.html