zoukankan      html  css  js  c++  java
  • <C++Primer>第四版 阅读笔记 第一部分 “基本语言”

    之前阅读时没有及时总结,现在慢慢补上。

    第1章 快速入门

    main 函数在很多方面都比较特别,其中最重要的是每个C++程序必须含有 main 函数,且 main 函数是(唯一)被操作系统显示调用的函数

    main 函数的返回值必须是 int 型


    endl 是一个特殊值,称为操纵符(manipulator),将它写入输出流时,具有输出换行的效果,并刷新与设备相关联的缓冲区(buffer)。通过刷新缓冲区,用户可立即看到写入到流中的输出。


    C++中通过定义来定义自己的数据结构。


    标准库的头文件用尖括号< >括起来,非标准库的头文件(自定义头文件)用双引号" "括起来。


    第2章 变量和基本类型

    要获得无符号型则必须指定该类型为unsigned。


    以0(零)开头的字面值整数常量表示八进制,以 0x 或 0X 开头的表示十六进制。


    为了兼容C语言,C++所有的字符串字面值都由编译器自动在末尾添加一个空字符(' ')。


    在C++中理解“初始化不是赋值”是必要的:初始化指创建变量并给它赋初始值,而赋值则是擦除对象的当前值并用新值代替


    内置类型变量是否自动初始化取决于变量定义的位置。在函数体外定义的变量都初始化成0,在函数体里定义的内置类型变量不进行自动初始化。


    变量的定义用于为变量分配存储空间,还可以为变量指定初始值。

    声明用于向程序表明变量的类型和名字。定义也是声明:当定义变量时我们声明了它的类型和名字。可以通过使用 extern 关键字声明变量名而不定义它。

    任何在多个文件中使用的变量都需要有与定义分离的声明。在这种情况下,一个文件含有变量的定义,使用该变量的其他文件则包含该变量的声明(而不是定义)。


    与其他变量不同,除非特别说明,在全局作用域声明的 const 变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。通过指定 const 变量为 extern ,就可以在整个程序中访问 const 对象。


    引用只是对象的另一个名字


    const 引用是指向 const 对象的引用。例:

    const int ival = 1024;
    const int &refVal = ival ;  //ok
    int &ref2 = ival ;  //error

    枚举的定义包括关键字 enum ,其后是一个可选的枚举类型名,和一个用花括号括起来、用逗号分开的枚举成员列表。

    enum open_modes { input, output, append };

    默认地,第一个枚举成员赋值为0,后面的每个枚举成员赋的值比前面的大1。

    用来初始化枚举成员的值必须是一个常量表达式。

    不能改变枚举成员的值。枚举成员本身就是一个常量表达式。


    如果使用 class 关键字来定义类,那么定义在第一个访问标号前的任何成员都隐式指定为private ;如果使用struct 关键字,那么这些成员都是public


    设计头文件时,应使其可以多次包含在同一源文件中,这一点很重要。我们必须保证多次包含同一头文件不会引起该头文件定义的类和对象被多次定义。使得头文件安全的通用做法,是使用预处理器定义头文件保护符。头文件保护符用于避免在已经见到头文件的情况下重新处理该头文件的内容。

    为了避免名字冲突,预处理器变量经常用全大写字母表示。

    #ifndef SALESITEM_H
    #define SALESITEM_H
    // Define````````
    #endif


    第3章 标准库类型

    任何存储 string 的 size 操作结果的变量必须为 string::size_type 类型。特别重要的是,不要把 size 的返回值赋给一个 int 变量。

    保存一个 string 对象 size 的最安全的方法就是使用标准库类型 string::size_type。


    当进行 string 对象和字符串字面值混合连接操作时,+操作符的左右操作数必须至少有一个是 string 类型的

    string s1="hello";
    string s2="world";
    string s3=s1+",";     //ok
    string s4="hello"+", "    //error
    string s5=s1+", "+"world";    //ok
    string s6="hello"+", "+s2;  //error

    vector 元素的位置从0开始。

    必须是已存在的元素才能用下标操作符进行索引。通过下标操作进行赋值时,不会添加任何元素

    所谓的“缓冲区溢出”错误就是对不存在的元素进行下标操作的结果。


    由于容器的 end 操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。


    容器定义了一种名为 const_iterator 的类型,该类型只能用于读取容器内元素,但不能改变其值。


    任何改变 vector 长度的操作都会使已存在的迭代器失效。例如,在调用 push_back 之后,就不能再信赖指向 vector 的迭代器的值了。

    两个迭代器相加的操作是未定义的


    第4章 数组和指针

    区别:数组的长度是固定的,数组一经创建,就不允许添加新的元素。指针则可以像迭代器一样用于遍历和检查数组中的元素

    现代C++程序应尽量使用 vector 和迭代器类型,而避免使用低级的数组和指针。设计良好的程序只有在强调速度时才在类实现的内部使用数组和指针


    与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,而且程序员无法知道一个给定数组的长度。数组没有获取其容量大小的 size 操作,也不提供 push_back 操作在其中自动添加元素。如果需要更改数组的长度,程序员只能创建一个更大的新数组,然后把原数组的所有元素复制到新数组空间中去。


    非 const 变量以及要到运行阶段才知道其值的 const 变量都不能用于定义数组的维数。


    与 vector 不同,一个数组不能用另外一个数组初始化,也不能将一个数组赋值给另一个数组


    C++提供了一种特殊的指针类型 void*,它可以保存任何类型对象的地址。

    void* 指针只支持几种有限的操作:与另一个指针进行比较;向函数传递 void* 指针或从函数返回 void* 指针;给另一个 void* 指针赋值。不允许使用 void* 指针操纵它所指向的对象。

    不能使用 void* 指针保存 const 对象的地址,而必须使用 const void* 类型的指针保存 const 对象的地址。


    在使用下标访问数组时,实际上是对指向数组元素的指针做下标操作。只要指针指向数组元素,就可以对它进行下标操作。例:

    int ia[]={0,2,4,6,8};
    int *p=&ia[2];
    int j=p[1];   //ok, j=ia[3]
    int k=p[-2];   //ok, k=ia[0]


    C++允许计算数组或对象的超出末端的地址,但不允许对此地址进行解引用操作。


    指针和 const 限定符

      1、指向 const 对象的指针

            如果指针指向 const 对象,则不允许指针来改变其所指的 const 值。为了保证这个特性,C++语言强制要求指向 const 对象的指针也必须具有 const 特性:

    const double *cptr ;

      2、const 指针

           除指向 const 对象的指针外,C++语言还提供 const 指针———本身的值不能修改:

    int errNumb=0;
    int *const curErr=&errNumb;
          const 指针的值不能修改,这就意味着不能使 curErr 指向其他对象。

          与任何 const 量一样,const 指针也必须在定义时初始化。
     

    字符串字面值的类型就是 const char 类型的数组。

    牢记字符串必须以结束符 null 结束:

    char ca[]={'C','+','+'};   //not null-terminated
    cout << strlen(ca) << endl;    //disater: ca isn't null-terminated

    动态数组的定义:

    数组变量通过指定类型、数组名和维数来定义。而动态分配数组时,只需指定类型和数组长度,不必为数组对象命名,new 表达式返回指向新分配数组的第一个元素的指针:

    int *pia = new int[10];
    此 new 表达式分配了一个含有10个 int 型元素的数组,并返回指向该数组第一个元素的指针,此返回值初始化了指针 pia 。

    new 表达式需要指定指针类型以及在方括号中给出的数组维数,该维数可以是任意的复杂表达式。创建数组后,new 将返回指向数组第一个元素的指针。在自由存储区中创建的数组对象是没有名字的,程序员只能通过其地址间接访问堆中的对象。

    第5章 表达式

    逻辑“”和逻辑“”操作符总是先计算其左操作数,然后再计算其右操作数。只有在仅考左操作数的值无法确定该逻辑表达式的结果时,才会求解其右操作数。我们常常称这种求值策略为“短路求值”。

    当表达式含有多个赋值操作符时,从右向左结合。

    后自增操作的优先级高于解引用操作,因此 *iter++ 等效于 *(iter++)。

    逗号表达式是一组由逗号分隔的表达式,这些表达式从左向右计算。逗号表达式的结果是其最右边表达式的值。

    值初始化的 () 语法必须置于类型名后面,而不是变量后。

    如果指针指向不是用 new 分配的内存地址,则在该指针上使用 delete 是不合法的。

    删除指针后,该指针变成悬垂指针。悬垂指针指向曾经存放对象的内存,但该对象已经不再存在了。悬垂指针往往导致程序错误,而且很难检测出来。
    一旦删除了指针所指向的对象,立即将指针置为0,这样就非常清楚地表明指针不再指向任何对象。

    第6章 语句

    在使用 switch 语句时,每个 case 标号的值都必须是一个常量表达式。如果表达式与其中一个 case 标号的值匹配,则程序将从该标号后面的第一个语句开始依次执行各个语句,直到 switch 结束或遇到 break 语句为止。例如一个计数元音个数的代码缺少break:
    switch (ch)
    {
        case 'a':   ++aCnt;    // not have a break statement
        case 'e':   ++eCnt;    // not have a break statement
        case 'i':   ++iCnt;    // not have a break statement
        case 'o':   ++oCnt;    // not have a break statement
        case 'u':   ++uCnt;    // not have a break statement
    }
    对于上面的代码,假设 ch 的值是 ‘i’,程序从 case 'i' 后面的语句开始执行,iCnt 的值加1。但是,程序的执行并没有在这里停止,而是越过case 标号继续执行,同时将 oCnt 和 uCnt 的值都加了1。

    对于 switch 结构,漏写 break 语句时常见的程序错误。

    如果所有的 case 标号与 switch 表达式的值都不匹配,并且 default 标号存在,则执行 default 标号后面的语句。

    对于 switch 结构,只能在它的最后一个 case 标号或 default 标号后面定义变量,制定这个规则是为了避免出现代码跳过变量的定义和初始化的情况。如果需要为某个特殊的case 定义变量,则可以引入块语句(即加{ }),在该块语句中定义变量,从而保证这个变量在使用前被定义和初始化。

    使用文件结束符(Ctrl+Z)来控制元素输入的结束时,若要进行第二个输入循环,则在此之前要将流 cin 恢复为有效状态(使用 cin.clear() )

    break 语句用于结束最近的 while、do while 、for 或 switch 语句。

    assert 仅用于检查确实不可能的条件,这只对程序的调试有帮助,但不能用来代替运行时的逻辑检查, 也不能代替对程序可能产生的错误的检测。

    第7章 函数

    辗转相除法求两个 int 型数的最大公约数
    int gcd(int v1, int v2)
    {
        while (v2)
        {
             int temp=v2;
             v2=v1%v2;
             v1=temp;
        }
        return v1;
    }

    函数不能返回另一个函数或者内置数组类型,但可以返回指向函数的指针,或指向数组元素的指针的指针。

    定义函数是,若使用普通的非引用类型的参数,参数通过复制对应的实参实现初始化。当用实参副本初始化形参时,函数并没有访问调用所传递的实参本身,因此不会修改实参的值。

    可以将指向 const 对象的指针初始化为指向非 const 对象,但不可以让指向非 const 对象的指针指向 const 对象。

    定义函数的一些小知识:
    1、使用引用形参返回额外的信息
    2、利用 const 引用避免复制
    如果使用引用形参的唯一目的是避免复制实参,则应将形参定义为 const 引用。

    不带返回值的 return 语句只能用于返回类型为 void 的函数。在返回类型为 void 的函数中,return 返回语句不是必须的,隐式的 return 发生在函数的最后一个语句完成时。
    一般情况下,返回类型是 void 的函数使用 return 语句是为了引起函数的强制结束,这种 return 的用法类似于循环结构中的 break 语句的作用。

    直接或间接调用自己的函数称为递归函数,递归函数必须定义一个终止条件,否则函数就会“永远”递归下去,这意味着函数会一直调用自身直到程序栈耗尽。
    注:主函数 main 不能调用自身

    函数声明
    函数声明中的形参名会被忽略,如果在声明中给出了新参的名字,它应该用作辅助文档:
    void print(int *array, int size);
    变量可以在头文件中声明,而在源文件中定义。同理,函数也应当在头文件中声明,并在源文件中定义。
    定义函数的源文件应包含声明该函数的头文件
    局部对象
    名字的作用域指的是知道该名字的程序文本区。对象的生命期则是在程序执行过程中对象存在的时间。
    只有当定义它的函数被调用时才存在的对象称为自动对象,自动对象在每次调用函数时创建和撤销。

    静态局部变量:一个变量如果位于函数的作用域内,但生命期却跨越了这个函数的多次调用,这种变量往往很有用。则应该将这样的对象定义为 static (静态的)。
    static 局部对象(static local object )确保不迟于在程序执行流程第一次经过该对象的定义语句时进行初始化。这种对象一旦被创建,在程序结束前都不会被撤销。当定义静态局部对象的函数结束时,静态局部对象不会撤销。
    内联函数
    调用函数比求解等价表达式要慢得多。内联函数避免函数调用的开销

    在函数返回类型前加上关键字 inline 就可以将函数指定为内联函数。

    一般来说,内联机制适用于优化小的、只有几行的而且经常被调用的函数,大多数的编译器都不支持递归函数的内联。

    内联函数应该在头文件中定义,这一点不同于其他函数。

    重载函数
    出现在相同作用域中的两个函数,如果具有相同的名字而形参表不同,则称为重载函数
    如果局部地声明一个函数,则该函数将屏蔽而不是重载在外层作用域中声明的同名函数。


    补充:区分 const* 与*const
    const int* p=&a;
    int const* p=&a;
    上面这两个功能相同,都是指针指向的内容不能被修改。

    int* const p=&a;
    指针p本身的值不能被修改。

    第8章 标准IO库

    IO类型在三个独立的头文件中定义:iostream 定义读写控制窗口的类型, fstream 定义读写已命名文件的类型,而 sstream 所定义的类型则用于读写存储在内存中的 string 对象。

    IO对象不可复制或赋值

    逗号操作符的求解过程:首先计算它的每一个操作数,然后返回最右边操作数作为整个操作的结果。

    C++中的文件名
    由于历史原因,IO标准库使用C风格字符串而不是C++ string 类型的字符串作为文件名。在创建 fstream 对象时,如果调用 open 或使用文件名作初始化式,需要传递的实参应为C风格字符串,而不是标准库 string 对象。程序常常从标准输入获得文件名。通常,比较好的方法是将文件命读入 string 对象,而不是C风格字符数组。假设要使用的文件命保存在 string 对象中,则可调用 c_str 成员获取C风格字符串。

    fstream 对象一旦打开,就保持与指定的文件相关联。如果要把 fstream 对象与另一个不同的文件关联,则必须先关闭(close)现在的文件,然后打开(open)另一个文件。

    如果程序员需要重用文件流读写多个文件,必须在读另一个文件之前调用 clear 清除该流的状态。
  • 相关阅读:
    【每天一道PAT】1001 A+B Format
    C++ STL总结
    开篇
    happen-before原则
    java多线程的状态转换以及基本操作
    集合初始容量
    fail-fast机制
    Stack
    Iterator
    Vector
  • 原文地址:https://www.cnblogs.com/fengty90/p/3768868.html
Copyright © 2011-2022 走看看