【Accelerated C++】重点回顾

来源:互联网 发布:nginx 多域名配置文件 编辑:程序博客网 时间:2024/04/29 09:26

看了刘未鹏推荐过的C++入门经典《Accelerated C++》,读此书的过程中一再感叹“大师就是大师,他可以很轻松的把东西的本质呈现在你面前”,这本书采用了现实中与我们很接近的学生成绩信息管理这一例子,层层推进,一步一步引出C++中的知识点,根据任务提出解决方案,让你感受到C++中一些设计的比要性。

一下是我读此书时做的笔记,谨用于记忆。


第一章、使用字符串


1、为什么需要缓冲?

    输入操作的副作用:它总是询问用户名字的请求显示在计算机的输出设备上。
    不管还有多少字符等待输入,很多系统在向输出设备写入字符时需要花费大量的时间。
    为了避免无法及时响应每个输出请求,标准库使用了缓冲来积累需要输出的东西,然后等到必要的时候,刷新缓冲来把缓冲里面的内容写到输出设备中。
    这样就可以通过一次写入完成多次输出操作。

2、促使系统刷新缓冲的三个事件:
    1)、缓冲满了。
    2)、标准库被要求读取标准输入流。
    3)、显示的刷新缓冲。

3、应该适时的刷新输出缓冲,防止输出拥塞在系统缓冲中。

4、如果一个变量是常量(const),必须在定义的时候初始化,否则没有机会赋值了。
    但是,初始化时const变量的值,可以不是常数(constant)。

5、操作符的一个永远不变的特性就是它的结合性。


第三章、使用批量数据


1、读取流失败的原因:
    1)、遇到输入文件的结束标志
    2)、输入的值的类型和需要读入的变量的类型不兼容。
    3)、系统可能检测到输入设备的硬件故障。

2、应该用size_type(无符号整型),用来表示容器大小-----这是一个好习惯。
    如: typedef vector<double>::size_type vec;

3、vector类型定义:
    v.end() 返回v中最后一个元素的‘下一个位置’。

4、while(cin>>x) 
    把适当类型的值读入x中,并且检测流的状态。
    如果流出错,检测失败;否则,检测成功,然后执行while语句体。
    

第四章、组织程序和数据


1、引用
    vector<double> homework;
    vector<double>& hw = homework;
    
    hw是homework的别名。对hw的任何操作都等价于对homework的同样操作,反之亦然。

    const vector<double>& chw = homework;

    chw仍然是homework的别名。但是const保证我们不会chw作任何改变它的值的操作。
    因为引用是原先对象的别名,所以不存在引用的引用。

    const vector<double>& chw1 = chw;
    定义一个引用的引用和定义一个人原先对象的引用效果相同。

    const vector<double>&, 意思是要求系统允许我们直接访问相关的实参,而不是复制实参,并且保证不会改变形参的值(否则实参的值也会被改变)。

2、左值
    一个左值是一个非临时性的对象。
    比如:如果一个变量是一个引用,或者是调用可以返回引用的函数的结果,那么这个变量就是一个左值。

3、read_hw( cin, homework );

    istream& read_hw( istream& in, vector<double>& hw )
    {
        if( in ){
            hw.clear();
            hw.push_back(1);

            in.clear();
        }
        return in; 
    } 


4、3种函数形参
    1)、vector<double>          -->复制实参(即便实参是一个很大的vector对象,效率低下)---不会改变vector对象本身。
    2)、const vector<double>&   --> &表明无须复制实参(效率高),const保证不会改变形参 ---简单的内置类型(如:int,double)没有必要使用const &
    3)、vector<double>&         -->非常量引用
    
    与非常量引用的形参对应的实参必须是一个‘左值’(非临时性的对象),而值传递与常量引用对应的实参可以是任意值。


5、一个好的经验:避免一个单独的语句产生多个副作用。抛出一个异常是一个副作用,所以抛出一个异常的语句不应该再产生其他副作用,尤其是输入和输出。

6、谓词。
    谓词就是可以产生真假值的函数。如下sort函数的谓词:
        bool compare( const Student_info& x, const Student_info& y )
        {
            return x.name < y.name; 
        }

        sort( student.begin(), student.end(), compare ); 



第五章、使用序列式容器并分析字符


1、迭代器
    一个迭代器是一个值,它能够:
        1)、标识一个容器和容易中的一个元素
        2)、允许检测元素中保存的值
        3)、提供在容器元素之间移动的操作
        4)、使用容器可用有效处理的方式来约束可用的操作
    因为迭代器的行为类似索引,所以我们常常可用把使用索引的程序重写为使用迭代器。

2、迭代器操作:
    在一个迭代器上使用*(解引用操作符),会返回一个左值,也就是这个迭代器指向的元素:
        cout<< (*iter).name; 
        如何是*iter.name,编译器会误以为是*(iter.name)
        但是可以使用:iter->name 替换


3、删除iter的后果:
    删除iter表示的元素会使这个迭代器无效。
    
    如果一个元素移动了位置那么指向它的任何一个迭代器都是无意义的--> 
        在一个vector的对象上调用erase,会使被删除元素之后的所有元素的迭代器都无效。
    所以:可以iter = student。erase(iter); 指向删除后的元素。


4、vector
    vector提供库容器中功能最强大的迭代器--->随机访问迭代器。
    
    虽然使用vector时,采用的都是动态分配元素空间,但是vector提供了预先分哦元素空间的机制以及指示vector分配空间的一个操作,但是没有使用额外的空间,这样是为了避免重复分配空间的开销。
    
    v.reserve(n) --预留空间---并不初始化它们。这个操作不改变容器的长度。它影响的知识调用insert或者push_back函数时,vector分配内存的频率。
    v.resize(n)  --给v一个等于n的新长度。如果n比v当前的长度小,n以后的元素就都会被删除。若n比当前长度大,新的元素会被添加到v中,并且会根据v包含的类型来适当的初始化。


5、list与vector的区别:
    list的结构复杂,比起vector顺序访问上慢一些。--->在末尾插入和删除的时候,vector好过list,但是在容器的中间删除很多元素时,list快很多。
    list不支持索引。
    给vector添加一个元素时,为新元素分配空间可能会引起整个vector的重新分配。

    由于list不支持随机访问,因此它不能使用标准库的sort,但是它有自己的sort:
        list<Student_info> student;  student.sort(compare);     


第六章、使用库算法


1、每个容器(也包括string类)都提供了相应的迭代器类型。迭代器使我们可以遍历一个容器,并且检测其中的元素。热情,标准库保证了每种迭代器都用相同的接口来操作。

2、与容器和迭代器相同,算法也使用了一致的接口规约。

3、copy( bottom.begin(), bottom.end(), back_inserter(ret) ); 
    copy是一个泛型算法, back_inserter 是一个迭代器适配器。

    泛型算法:一种不属于任何特殊容器的算法,它可以从其参数类型知道如何访问它使用的数据。

4、copy( begin, end, out ); ---> while( begin != end ) *out++ = *begin++; 
    唯一的区别是while语句体改变了迭代器的值,但是copy算法没有改变。

5、it = begin++; 等价于:it = begin; ++begin; 

6、不能把重载函数作为参数传递给一个模板函数。

7、算法、容器和迭代器
    算法是作用在容器元素上的----它们并不作用在容器上。
    
8、迭代器适配器:
    是产生迭代器的函数。
    最常用的是生成insert_iterator的适配器,它生成的迭代器可以动态的增长关联的容器。这样的跌大全可以安全的用作复制类型的算法的目的地。    


第七章、使用关联式容器


1、序列式容器
    序列式容器中的元素都是按照指定的次序来排列。当在push_back或者插入新的元素时候,每个元素的位置都不会变化,除非对容器进行一些重新排序的操作。

2、关联式容器
    最常见的关联数据结构是‘键值对’,每个值都与独一无二的键相对应,这样我们可以根据元素的键,来快速的插入或者取回对应的元素。

3、两者的区别:
    关联式容器是自排列的。

4、map
    map<string, int> counters; 
    (string: 键, int: 值 ) 称这个容器是string映射到int的map。
    
    键总是const型的--防止被隐式的改变元素在map中的位置。

5、关联数组的性能:
    这些数组很可能是按照一种叫做哈希表的数据结构来实现的。哈希表速度很快,但是也为此付出了一些代价:
        1)对于每种类型的键,人们都必须提供一个哈希函数,根据键的值,来计算出一个合适的整数。
        2)哈希表的性能与哈希函数的实现细节密切相关。
        3)一般来说,很难按照有用的顺序重新取得哈希表的元素。C++的关联式容器很难按照哈希表来实现。
        4)键类型需要实现的只是<操作符或者是相等性比较函数。
        5)使用给定的键来访问关联式容器中的元素所消耗的时间是容器中元素总数的对数,而不管键的值是什么。
        6)关联式容器总是根据键来排序。

        换句话说,虽然c++关联式容器明显比最好的哈希表数据结构慢,但是它的性能却要比其他的数据结构好的多,而且由于他们自动排序的机制,所以他们也要比哈希表方便很多。
    
    C++库使用的是一种平衡的,可以自我调节的树结构来实现关联式容器。

6、访问一个不存在的键,会自动生成这个键及值。





(未完,待续。。。)

from: http://blog.csdn.net/cyh_24/article/details/8239633
0 0
原创粉丝点击