C++ primer读书笔记 part 1

C++基础(1~7)

第1章 开始

1.1

  • 在UNIX和Windows系统中,执行完一个程序后,都可以通过echo命令获取其返回值:

    UNIX:

    $ echo $?
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-77UZF17I-1588660354488)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200410083217521.png)]

    Windows:

    $echo %ERRORLEVEL%
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hJIdpMFy-1588660354498)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200410082302097.png)]

1.2 初识输入输出

  • std::endl(缓冲刷新操作符)的效果是结束当前行,并将与设备关联的缓冲区中的内容刷到设备中。

1.4 控制流

  • 读取数量不定的输入数据:

    cin语句当遇到EOF(文件结束符,UNIX下ctrl+D可结束),或遇到无效输入时会将返回的istream状态变为无效。

    int main() {
        int value;
        while (std::cin >> value)
            cout << value << " ";
        cout << endl;
    }
    

第2章 变量和基本类型

2.1 基本内置类型

  • char类型实际上有可能会表现为signed char或unsigned char,由编译器决定。

  • 如何选择内置类型:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zITVbrmB-1588660354504)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200410091439378.png)]

  • 当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数;而当赋给带符号类型一个超出它范围的值时,结果是未定义的(这里有问题,实际上当给两者赋予超出范围值都是未定义的,但这个范围与它们能表示的最大值决定,如signed char表示-128~127,所以128表示的是-128,255表示-1,256则超出范围,这都与有符合以补码形式保存有关)。

  • 当一个算术表达式上又有无符号数又有int值,int值会转换为无符号数。

2.2 变量

  • 分离式编译:如果想声明一个变量而非定义它,就在变量名前添加关键字extern,且不要显示初始化(否则会变为定义)。

  • 在函数体内部,不能初始化一个由extern关键字标记的变量,会报错(为什么?)。

  • 建议:第一次使用变量时才定义它。

  • 列表初始化:

    // 首先明确四种初始化语句
    int a = 0;
    int a = {0};// 这种就是列表初始化
    int a{0};	// 这种就是列表初始化
    int a(0);
    
    long double ld = 3.1415926536;
    int a = {ld};	// 错误:存在丢失信息危险会报错
    int b{ld};		// 错误:存在丢失信息危险会报错
    int c = ld;		// 正确
    

2.3 复合类型(引用和指针)

  • tips:从右向左阅读复杂的类型声明语句,更容易读出它的类型。‘

2.4 const

  • const对象仅在文件内有效:当多个文件中出现了同名的const变量时,其实等同于在不同文件分布定义了独立的变量。

    如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字:

    // file_1.cpp定义并初始化了一个常量,该常量能被其他文件访问
    extern const int bufSize = fcn();
    // file_l.h头文件声明
    extern const int bufSize;
    
  • const引用(指针也是如此)允许引用类型不一致,只要该表达式的结果能转换成引用的类型:

double dval = 3.14;
const int &ri = dval;

其上相当于生成了一个const int类型的临时量对象,所以不允许非const引用引用这个临时量对象。

指针和引用都规定,类型必须与所指对象的类型一致,以上是一个例外,还有另一种例外就是向下转型。

  • 顶层const和底层const

  • constexpr:将变量声明为constexpr类型,编译器则可以验证变量是否为常量表达式(不是则报错)。

    constexpr会将指针变量都声明为顶层const

2.5 处理类型

类型别名
  • typedef:typedef double wages, *pd;
  • 新标准规定的别名声明:using wages = double;
auto类型说明符
  • auto能让编译器替我们去分析表达式所属的类型。
  • auto定义的变量必须有初始值。
  • auto会忽略顶层const,保留底层const(也很好理解为什么),如果希望是一个顶层const,则显式const(引用也会被忽略,也是通过显式声明)。
decltype类型指示符
  • decltype的作用式选择并返回操作数的数据类型(仅分析类型,不计算表达式的值):decltype(f()) sum = x;

  • decltype不会忽略顶层const和引用,也就是它返回是实际类型,而不是像auto选择更适合的类型进行优化。

  • int i = 42, &r = i, *p = &i;
    decltype(r + 0) b;	//这时类型就不是引用而是int了
    decltype(*p) c = 1;	//如果表达式的内容是解引用操作,则会得到引用类型,所以这里为int &类型
    
  • decltype((varible))的结果永远是引用。给变量加上一层或多层括号,编译器就会把它当成一个表达式,变量是一种可以作为赋值语句左值的特殊表达式,所以这样的decltype就会得到引用类型。

第3章 字符串、向量和数组

3.2 标准库类型string

  • getline(cin, line)

    string line;
    while (getline(cin, line)) {
    	cout << line << endl;
    }
    
  • string::size_type类型:其size()函数的返回类型。

    这是个无符号数,注意避免与有符号数混用从而造成错误!

  • 标准库允许把字符字面值字符串字面值转换成string。

    string s1 = ",";
    string s = "abc" + s + 'd'; // 必须保证运算符两边有其一为string类型(非字面值)
    

    因为字符串字面值并不是string的对象(与C兼容)!

  • #include 定义了一组处理字符的函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fuo6fSFf-1588660354508)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200413201358523.png)]

3.3 标准库类型vector

  • 不能在范围for循环中向vector对象添加元素:在范围for语句中,预存了end()的值,添加或删除元素后,这个值就变为无效了。

3.4 迭代器介绍

  • 任何一种可能改变vector对象容量的操作(如push_back)都会使该vector对象的迭代器失效。

    谨记,但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素!

  • defference_type:带符号整数,表示迭代器的距离,iter1 - iter2返回该类型。

3.5 数组

  • 默认情况下,数组的元素被默认初始化。
  • 数组间不允许拷贝和赋值。
  • 默认情况下,类型修饰符从右向左依次绑定,所以可以从右向左阅读来识别其类型。如 int *ptrs[10]为一个数组,该数组元素为指针(*),该指针指向int类型;而 int (*pArr)[10]首先为一个指针(括号括住),然后该指针指向一个数组([]),数组元素为int类型。
  • 指针与数组:一般来说使用数组名作为赋值,都会将其替换成指向该数组首元素的指针。不过在使用decltype时却会帮其转换成数组类型。(就很神奇,多维数组的数组名则是替换成指向该数组首数组的指针)。
  • 只要两个指针指向同一数组就可以进行比较以及进行相减(返回ptrdiff_t类型,带符号类型)。

3.6 多维数组

  • 使用范围for处理多维数组:

    for (auto &row : ia) {	// 注意这里必须使用引用,否则row的类型是指针而不是数组,会导致后面无法使用范围for
    	for (auto col : row) {
    		...
    	}
    }
    
  • 类型别名多维数组的指针:

    using int_array = int[4];	// 两者皆可
    typedef int int_array[4];
    

第4章 表达式

4.1 基础

  • 左值和右值:当一个对象被用作右值时,用的是对象的值(内容);当对象被用作左值时,用的时对象的身份(在内存的位置)。
  • 使用关键字decltype时,如果表达式结果是左值,则返回一个引用类型,否则返回原类型。
  • 只有4种运算符明确规定了求值顺序:&&||?:,

4.5 递增和递减运算符

  • 递增的后置版本优先级高于解引用运算符;但前置版本好像是相同的,遵循右结合律。

4.7 条件运算符(?:)

  • 条件运算符的优先级比较低,所以要注意加括号!!

    cout << (grade < 60 ? "fail" : "pass") << endl;	// 输出条件运算的结果
    cout << (grade < 60) ? "fail" : "pass" << endl;	// 输出1或者0,然后cout进行条件运算
    cout << grade < 60 ? "fail" : "pass" << endl;
    // 输出grade,然后cout<60进行条件运算
    

4.8 位运算符

  • 位运算对于有符合数的符号位如何处理没有明确规定,是一种未定义行为,所以建议将位运算符用于处理无符号数。
  • 要注意<<和>>的优先级可能比较高,在作为IO运算符时要注意为一些运算加括号,避免错误。

4.11 类型转换

  • 算术类型之间的隐式转换会尽可能避免损失精度,如果表达式种既有浮点数又有整数,会统一为浮点数。
  • 在大多数表达式中,比int类型小的整数型会首先提升为较大的整数类型(如short、char以及bool等都会转成int类型)。转换为int、unsigned int、long、unsigned long、long long和unsigned long long中能容纳其全部可能的值的最小类型。
  • 如果一个运算对象为无符号,另一个为有符号,且其中无符号类型不小于带符号类型,那么带符号转为无符号的;如果大于,则看带符号类型是否包含无符号的所有值,否则同样转为无符号。
  • static_cast会关闭精度损失的警告信息,其实用起来挺危险的。static_cast还可以将void *转为任意类型指针(隐式不行)。

第5章 语句

5.3 条件语句

  • switch内部的变量定义:如果在某处一个带有初值的变量位于作用域之外,在另一处该变量位于作用域之内,则从前一处跳转到后一处的行为是非法行为。

5.6 try语句块和异常处理

  • throw表达式引发一个异常,try检测,catch处理。
  • 最终没有被处理的异常,程序会转到名为terminate的标准库函数,没有try的语句块发生异常也是如此。
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iPoPZQ15-1588660354510)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200421090205691.png)]

第6章 函数

6.2 参数传递

  • 使用引用避免拷贝:建议使用常量引用。

  • 返回额外信息:比较简单的方法,使用引用参数。

  • 由于顶层const会被忽略,所以const int和int是同一种参数列表。

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RnA9fKdu-1588660354515)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200421102119636.png)]

  • 数组引用形参:void func(int (&arr)[10])

  • main处理命令行选项:第一个形参argc表示数组中字符串的数量;第二个形参为字符串指针,第一个指针指向程序名,最后一个指针必定为0(不包含在argc数量里面)。

  • initializer_list实现可变形参,有点像vector。

  • 省略符(…)形参:varargs来访问省略的形参所接受的实参。

6.3 返回类型和return语句

  • 函数的返回类型为引用时得到左值,否则得到右值。

  • 返回数组指针:可以使用类型别名来避免出错。不使用的话如下:

    int (*func(int i))[10];
    
  • 尾置返回类型:

    auto func(int i) -> int(*)[10]
    

6.4 函数重载

  • const_cast和重载:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yuJz6ejo-1588660354519)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200422172617493.png)]

  • 如果在内层作用域声明名字,它讲隐藏外层同名声明,从而导致忽略掉其他同名函数重载。

6.5 特殊用途语言特性

默认实参

  • 默认实参负责填补函数调用的尾部实参,所以提供实参的这个参数前面的参数也必须提供实参,否则报错。
  • 局部变量不能作为默认实参(全局变量,调用函数都可)。

constexpr函数

  • 指能用于常量表达式的函数,函数的返回类型和所有形参的类型都是字面量类型,且有且只有一条return语句。
  • 内联函数和constexpr函数通常放在头文件内,因为他们需要在程序中多次定义(为了让每个文件独立编译时,编译器可以展开函数),并且多个定义完全一致。

6.6 函数匹配

  • 如果有且只有一个函数满足下列条件,则匹配成功:

    • 该函数每个实参的匹配都不劣于其他可行函数需要的匹配。
    • 至少有一个实参的匹配优于其他可行函数提供的匹配。
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wVbcTqvG-1588660354521)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200421165123949.png)]

6.7 函数指针

  • 声明方式:只需要用指针替换函数名即可。

    // 函数指针
    bool (*pf)(const string &, const string &);	
    // 函数
    bool lengthCompare(const string &, const string &);	
    
  • 当把函数名作为一个值使用时,会自动转换成指针(使用取地址符也可);对于函数指针也是如此,会自动转换成函数直接使用。

  • 函数指针可以作为形参,也可作为返回值。

第7章 类

7.1 定义抽象数据类型

  • 编译器创建的构造函数被称为合成的默认构造函数,他会默认初始化数据成员(除非存在类内初始值,就是定义数据成员就赋值)。
  • 只有类没有声明任何构造函数时,编译器才会自动生成默认构造函数。
  • 定义在类内部的成员函数都是自动inline的。

7.3 类的其他特性

  • 可变数据成员:使用mutable修饰数据成员,该成员可以在一个const成员函数中被改变。
  • 返回*this的成员函数:返回类型必须是引用类型,这样就能实现链式操作。
  • 基于const的重载:对于一个const的成员函数,返回的*this也必须是const引用的,所以可以重载该成员函数。通过对象本身是否为const决定调用哪个成员函数,从而也能够实现链式调用。

7.5 构造函数再探

  • 如果成员是const或者是引用的话,或者成员类类型没有定义默认构造函数,都必须在构造函数上使用初始化列表为这些成员提供初值(而不能在函数体进行赋值操作)。

    建议:使用构造函数初始值。

  • 构造函数初始化列表的初始化顺序,与他们被定义的顺序有关,而与他们在初始化列表中的顺序无关。

  • 委托构造函数:

    Sales_data(): Sales_data("", 0, 0); 
    // Sales_data(string, unsigned, double)是一个已经被定义的构造函数。
    
  • 转换构造函数:当类中的构造函数只接受一个实参时,实际上定义了该实参类型转换为此类类型的隐式转换机制。例如,定义了Sales_data(string ),则可以使string类型的变量隐式转换成Sales_data

  • 上述转换只允许一步。例如,char *能转为string,此时不能将char *直接转为Sales_data

  • 通过explicit关键字可以阻止含有单实参的构造函数所带来的隐式转换(仍可以显示转换static_cast)。并且该构造函数只能以直接初始化的方式使用。

  • 聚合类与字面值常量类(暂时略过)。

7.6 类的静态成员

  • 静态数据成员可以是不完全类型(特别的,静态数据成员可以是它所属的类)。静态成员还能作为默认实参(非静态不能的原因是它的值本身就属于对象的一部分,这样就不能提供一个对象以便从中获取成员的值)。

下一部分:C++标准库(8~12)

End

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值