《C++ Primer 第四版》笔记(1)

第一部分 基本语言

[第2章 变量和基本类型]
  C++内置类型包括bool、char、wchar_t、short、int、long int、float、double、long double,C++标准定义了每种内置类型至少应该占用的位数,具体占用多少位数由每个编译器自己决定,在VC++2008中,每个类型占用的字节数分别为:1、1、2、2、4、4、4、8、8。
    超过范围的数赋值给unsigned类型时,得到的值是这个数与这个unsigned类型变量最大数的模的结果,比如unsigned char a = 336; a的实际值是80。unsigned char b = -1; a的实际值是255。
    在字符常量前加L可把该常量转换为wchar_t类型,如L'a'。也可以在字符串常量前加L把它转换为宽字符串常量。
    在C++中,初始化和赋值是两种不同的操作,初始化指创建变量并给它赋初始值,而赋值是擦除变量的当前值并用新值代替。
    内置类型变量是否初始化取决于变量定义的位置,在函数体外定义的变量都初始化为0,在函数体里定义的变量不进行自动初始化,其值是未定义的。
   非const变量默认为extern,可以在别的文件中通过extern声明使用。但const变量默认不是extern的,要在别的文件中使用,除了在别的文件中需要用extern声明外,const变量在定义时也需要用extern。
    非const引用不能引用const变量,const引用可以引用非const变量;非const引用只能引用本类型变量,且不能用字面常量、算术表达式初始化;const引用可以引用相关类型变量,可以用字面常量、算术表达式初始化。
    枚举类型的对象初始化或赋值只能用本类型的枚举成员或本类型的枚举变量。

[第3章 标准库类型]
    string是C++的标准库类型,相比于内置类型,更加抽象、高级。string可以存储可变长度的字符串,使用者不需要负责内存管理;cin>>s;会略去前导和后续空格,s只保存连续非空字符;string包含了配套类型string::size_type,用于表示字符长度及字符索引的类型,是unsigned类型的(unsigned int 或unsigned long)。string的size()函数返回的
长度及字符下标索引最好用string::size_type类型的变量。

[第4章 数组和指针]
    现代C++应尽量使用vector和迭代器类型,尽量避免使用低级的数组和指针。
    不能定义引用数组,也没有引用的引用变量。
    数组的维数值应在编译时就能确定,因此数组的维数只允许用常量表达式定义,包括整型字面值常量、枚举常量、用常量表达式初始化的整型const变量。
    数组的下标类型是size_t,vector的下标类型是vector:size_type。
    指针的初始化及赋值可以用0值整型常量表达式(如0、值为0的const整型常变量),但不能用非0值及普通变量。
    const double *cptr1;    //cptr1是指向const double类型的指针,不能通过cptr1改变变量的值,但cptr1的值可以改变,使其指向其他变量;
    double *const cptr2;    //cptr2是指向double类型的常指针,可以通过cptr2改变变量的值,但cptr2本身的值不能改变,不能指向其他变量;
    const double *const cptr3;    //cptr3是指向const double类型的常指针,cptr3的值以及其指向的变量的值均不能改变。
    const对象的地址不能赋值给指向非const类型对象的指针;非const对象的地址可以赋值给指向const对象的指针,此时不能通过这个指针改变这个对象的值,但因为对象本身是非const的,因此对象的值仍能通过指向该对象的非const指针或者直接给该对象赋值改变。
    通常将指向const对象的指针作为函数形参,保证在函数内部不会通过这个指针值改变其指向对象的值。
    typedef string *pstring; const pstring cstr;    //等效形式不是const string *str; 而是string *const str; const pstring cstr;这句中,const修饰的是pstring类型,而pstring是string *指针类型,因此等效形式中const修饰的应该是指针。
    数组的维数应该在编译时就能确定,而用malloc和new动态分配的数组其维数可以在程序运行过程中确定,维数可以是任意复杂的表达式。
    动态分配数组时,如果数组元素类型是类类型,则将使用该类的默认构造函数初始化,如果数组元素是内置类型,则不进行初始化。
    可以在动态分配数组时在后面加括号显式初始化:int *p = new int[10]();把每个元素初始化为整型默认值0。用该方式初始化,只能初始化为数组类型默认的值,不能在最后的括号中指定数值为数组初始化,即使是int的默认类型0也不行。
    如果动态分配的数组元素是const类型的,则在定义时必须初始化:const int *p = new const int[10](); 而const类型的值不能改变,因此这种方式的定义没有太大用处。
    定义长度为0的数组是不允许的,但new动态分配数组时接受参数0作为数组长度,此时返回一个有效的非零指针,该指针与其他参数的new返回的指针不同,不能进行解引用操作,但可以用于比较。
    string的c_str()函数返回的是const char类型的C风格的数组,因此接受变量也应该是const类型的:string s = "abc"; const char *str = s.c_str();

[第5章 表达式]
    求余(求模)操作符%的操作数只能为整数,包括bool、char、short、int、long以及对应的unsigned类型。
    两个操作数都为整数时:若两个操作数都为正,则/和%的结果都为正;如果两个操作数都为负数,则/的结果为正,%的结果为负;如果只有一个操作数为负数,则/的结果为负,但具体数值是向0还是负无穷取整,则依赖于具体的机器,%的结果符号及数值都依赖于具体机器。
    &&及||操作符,仅当第一个操作数无法确定整个表达式的值时,才会计算第二个操作数。
    位操作符的操作数只能是整数,包括signed和unsigned类型。signed类型整数的位操作,符号位如何处理依赖于具体机器实现。
    位移操作符(左移和右移)的右操作数不能是负数,而且必须严格小于左操作数的位数,否则操作结果未定义。
    赋值表达式的值是其左操作数的值,结果类型为左操作数类型。赋值操作具有右结合性。因此可以使用连等赋值操作:string s1, s2; s1=s2="ok";
    逗号表达式的结果是其最右端表达式的值。
    用delete删除不是用new分配的空间时,操作非法,编译可能通过,但运行报错。比如:int a = 3; int *pa = &a; delete pa; 但如果指针值为0,用delete删除是合法的,不会报错,只不过这样做没有意义。
    用delete删除了一个指针指向的空间后,最好将指针值置为0,以免不小心引用这个悬挂的指针。
    指向任意数据类型的指针都可转换为void*类型,整型常量0可以转换为任意指针类型。
    C++自动将枚举类型的对象或枚举成员转换为整型,转换结果可以用于任何要求使用整数值的地方(非匹配枚举类型对象的初始化和赋值除外)。
    除了&&、||、和?:操作符,C++没有定义其他操作符的操作数的计算顺序,操作数的求解顺序由编译器决定。比如if(ia[index++] < ia[index]),<两边的操作数计算顺序影响最终比较结果,如果先计算左操作数,则结果相当于if(ia[index] < ia[index+1]);如果计算右操作数,则结果相当于if(ia[index] < ia[index])。因此如果一个表达式中一个变量出现了多次,而在某个子表达式中改变了该变量的值,则最好拆分表达式为多个,避免因计算顺序带来的二义性。比如,如果希望if(ia[index++] < ia[index])是先计算左值,则改为if(ia[index] < ia[index+1])... ++index;
    
[第6章  语句]
    用于switch判定的类型只能是整型值,包括bool、char、short、int、long以及相应的unsigned类型。
    可以在for语句头的第一部分定义多个对象(变量),但因为此处只能出现一个语句,因此所有的对象(变量)必须是同类型的。
    goto语句和跳转到的标号应该位于同一个函数中。不提倡使用goto语句,所有使用goto语句的程序都可以改写为不使用goto语句的程序。
    寻找处理throw对应消息的代码的过程与函数的调用过程相反,首先在当前函数中寻找有无匹配的catch子句,如果没有,则返回到调用这个函数的函数中寻找,如此过程进行。如果不存在处理该异常的catch子句,则系统调用标准库函数terminate。如果出现异常的代码没有放在try语句块中,则出现异常时直接调用terminate函数。terminate函数的行为依赖于系统,通常它的执行将导致程序非正常退出。
    assert宏可用于帮助程序调试。assert(expr);当expr为真时继续向前执行,否则程序终止运行。该宏只在debug版本中有效,在release版本中,该语句不工作。

[第7章  函数]
    早期的C++版本默认没有显式定义返回值类型的函数的返回值是int,但新版本的C++版本则要求显式定义函数的返回值类型。
    形参的初始化如果是非引用类型,则复制实参的值;如果是引用类型,则形参只是实参的别名。通过把形参定义为引用类型,可以改变实参的值,用于返回函数内部的值;还可以避免实参值的复制,否则大型实参对象的复制将非常耗资源。
    如果形参是引用类型,而函数不修改引用变量的值,则最好将该类型定义为const引用。因为非const引用类型要求实参是完全相同类型的非const对象(short 就不能赋值给int &),而且字面常量、const对象、产生右值的表达式均不能作为实参。
    如果要在函数内改变形参指针的值,可以把形参定义为指向指针的引用,如void swap(int *&p1, int *&p2)。
    数组形参是非引用类型时,编译器只检查实参是不是指针、指针的类型。实参是数组时,实参数组会自动转化为指向数组第一个元素的指针,形参接收这个指针副本,可以在函数内部修改这个指针值访问数组元素。
    数组形参是引用类型时,编译器不会将数组实参转换为指针,而是传递数组引用本身,此时数组大小成为形参和实参类型的一部分,形参只能接受对应大小的实参数组,编译器会检查数组实参的大小与形参的大小是否匹配。如void printValues(int (&arr)[10]){} 则实参只能是包含10个元素的数组。
    main函数可以不写返回语句(如return 0;),编译器会隐式添加返回0值的语句。
    不要返回指向函数内部局部对象的指针或者引用,函数返回时,局部对象被销毁,指针或引用指向的是不确定的内存单元。
    在调用函数之前应该有函数的声明。一个函数只能定义一次,但可以声明多次。一个较好的习惯是:在头文件中声明函数,在源文件中包含头文件并实现函数。这样可以保证函数的声明一致,函数接口改变时,只需要改动头文件中的一处声明即可。
    函数的默认实参应该排在形参列表的最后,一个好的习惯是把最少使用默认实参的形参排前面,最可能使用默认实参的形参排后面。
    默认实参可以是任何适当类型的表达式(包括函数调用)。默认实参既可以在函数声明中指定,也可以在函数定义中指定。但是,在一个文件中只能为一个形参指定默认形参一次。如果在函数定义时指定了默认实参,则只能在包含该函数定义的源文件中使用该默认实参。因此,一个好的习惯是:在函数声明时指定默认实参。
    内联机制适用于优化小的、只有几行的而且经常被调用的函数。大多数编译器都不支持递归函数的内联。内联函数应该在头文件中定义。编译器隐式地将在类体内定义的成员函数当做内联函数。
    main函数不能重载。
    如果形参是非引用(或指针)类型的,C++会忽略const形参修饰符,认为带const修饰和不带const修饰的是同一个函数。如果形参是引用(或指针)类型的,则带const修饰和不带const修饰的是两个不同的函数。
    C++的名字查找发生在类型检查之前。如果在外层作用域中声明了若干重载函数,而在内层作用域中声明了同名的函数,则内层作用域声明的这个函数将覆盖外层作用域中声明的函数,而不是对外层作用域声明函数的重载!即使在内层作用域中定义了一个和外层作用域函数同名的一个变量,这个变量也会覆盖外层作用域声明的函数。重载函数应该在同一个作用域中声明,不同作用域中的同名函数实现的是覆盖效果。
    如果有重载函数,编译器确定调用哪个函数的过程称为函数匹配(function matching),包括三个步骤:
        1、确定候选函数:确定函数名与调用函数相同的函数;
        2、选择可行函数。满足两个条件:形参实参个数匹配、形参实参类型匹配(精确匹配或可隐式转换);
        3、寻找最佳匹配。匹配优先度从高到低排列是:精确匹配、通过类型提升实现匹配、通过标准转换实现匹配、通过类类型转换实现匹配。
    函数指针只接受三类值的初始化或赋值:完全同类型的函数、函数指针、0值常量表达式。
    指向不同函数类型的指针之间不能相互转换。
    返回指向函数的指针:int (*ff(int))(int *, int); 函数名是ff,它的形参类型是int,返回一个函数指针,其类型是int (*) (int *, int)。
    
[第8章  标准IO库]
    IO类型在三个独立文件中定义:iostream、fstream、sstream,分别对应标准输入输出流、文件流、字符串流。
    C++定义了宽字符类型wchar_t,C++ 的IO库也提供了对宽字符类型的支持,在对应char类型的处理版本前加字母”w”,如定义了相应的类 wostream、wistream、wiostream、wifstream、wofstream、wfstream、wistringstream、wostringstream、wstringstream等,还有对应的对象wcin、wcout、wcerr等。
    标准库类型不支持赋值和复制操作,如果要传递或返回IO对象,则只能用引用或指针。
    每个IO对象对应一个缓冲区,如下几种情况将使对应缓冲区刷新:
        1、程序正常结束。
        2、缓冲区已满。
        3、用endl、ends、flush、unitbuf等操纵符显示地刷新缓冲区。
        4、将输出流与输入流关联起来,这样当读输入流时将刷新其关联的输出缓冲区。
    如果程序非正常结束,输出缓冲区将不会自动刷新!
    标准库默认已经将cout和cin绑定关联在一起。
    fstream对象一旦打开,将保持与指定的文件关联,如果要使这个对象与另一个文件关联,则应该先用close()方法关闭现有关联,再用open()方法关联新的文件。
    


你可能感兴趣的:(C++,C++)