C++ Primer读书笔记梳理系列(一)

本文摘录了C++ Primer第五版的关键知识点,涵盖变量、基本类型、字符串、向量、数组、表达式、函数、类等内容,深入解析了C++语言的特性与编程技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++ Primer读书笔记梳理系列(一)

第二章 变量和基本类型

引用和指针

  1. 引用就是对象的别名(不是对象),之后不能被修改,必须初始化,类型必须进准匹配,且不能与表达式的结果匹配

    int &ref1 = 10; //错
    double a = 3.14;
    int &ref2 = a; //错,类型没有精确匹配
    int b = 10;
    int &ref3 = a+10; //错,不能与表达式的计算结果绑定
    
  2. 指针与引用不同,是一个独立的对象,它的值是其他对象的地址

    注意:&符号在等号左边表示引用,在右边表示取地址符。

  3. 可以对指针进行引用(指针是对象),反之不行

const 限定符

  1. 可以参考Efective C++ 条款3

  2. 常量表达:只值不会被改变,并且在编译过程中就能够计算结果的表达式;表达式是不是常量表达式由它的数据类型和初始值共同决定

    const int a = 20; //是
    const int b = a+1; // 是
    int c = 27; //不是,因为int
    const int d = get_size(); //不是,因为在运行时才能得到d的值
    
  3. constexpr变量

  4. 系统很复杂时,我们很难分辨一个初始值到底是不是常量表达式,所以C++11新标准想了个办法,允许将变量声明为constexpr类型来让编译器验证变量的值是否是一个常量表达式

    constexpr int a = 20; //对
    constexpr int b = a + 1; //对
    constexpr int sz = size(); //只有当size是一个constexpr函数时才正确
    

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

命名空间的using声明

  1. 含using声明

    using namespace std; //这样就可以访问std中所有的名字,但是很多用不到,
    //可能程序开销会变大
    
  2. 头文件不应该包含using 声明

    我们用头文件一般是自定义数据类型吧(有待讨论) 因为头文件的内容在编译时会被拷贝到所有引用它的文件中去,如果头文件中有某个using声明,那么每个使用了该头文件的文件就都会有这个声明,很有可能产生一些冲突。

数组与多维数组

  1. 只有在用字符串字面值初始化数组时,编译器会在最后面加一个空字符’\0’

    char a1[] = {'C', '+', '+'}; //列表初始化,后面没有空字符
    char a2[] = "C++"; //自动添加空字符
    char a[3] = "C++"; //错误:维度不够,没办法存放空字符
    
  2. 编译器会自动将数组名字替换为指向数组首元素的指针

第四章 表达式

基础

  1. 这里重点讲一下左值与右值

    定义:当一个对象被用作右值的时候,用的是对象的值;当对象被用作左值的时候,用的是对象的身份。

    原则:在需要右值的地方可以用左值代替,但不能把右值当成左值使用,也就是说,身份可以当值,值不能表示身份。

    如何理解:身份就是内存中的位置,值就是内存中的内容,你知道了左值,也就是内存中的位置,当然可以拿到那个内容,所以左值能代替右值,但是你知道了内容,你是找不到地址的,因为可能很多内存地址的内容都是这个右值啊。

    赋值运算符需要左值作为其左侧运算对象,得到的结果也是左值
    取地址作用于左值,返回一个指向该运算对象的指针,这个指针是右值
    内置解引用,下标,迭代器解引用的求值结果都是左值
    递增递减作用于左值,前置版本例如++i得到结果也是左值

第五章 语句

try语句块和异常处理

​ 异常是指存在于运行时的反常行为,而且这些行为超出了函数正常功能的范围,就是说函数无法处理了,例如失去数据库连接以及遇到意外输入等。

  1. throw表达式

    if(item1.isbn() != item2.isbn())
    {
     throw runtime_error("不是同一种书");//一旦抛出异常后,程序就把控制权转移给能处理该异常的代码。 类型runtime_error是标准库异常类型的一种
    }
    cout << item1 + item2;
    
  2. try语句块

    
    while(cin >> item1 >> item2)
    {
     try //执行相加,不行抛异常
     {
         if(item1.isbn() != item2.isbn())
         {
             throw runtime_error("不是同一种书");
         }
         cout << item1 + item2;
     }
     catch(runtime_error err)
     {
         cout << err.what() << "再试一次?请输入yes或者 no" << endl;
         //what是库里的一个函数
         string str;
         cin >> str;
         if(!cin || str == "no") //如果没输入或者输入no,就跳出循环,结束程序
         {
             break;
         }
     }
    

    如果没有找到匹配的catch子句,程序会转到terminat库函数,导致程序非正常退出。

第六章函数

函数匹配

  1. 注意函数调用产生的二义性

    void f(int, double);
    void f(double, int);
    int main()
    {
        f(1,1); //调用有二义性
        return 0;
    }
    
  2. 函数指针

    函数指针指向的是函数,不是对象

第七章 类

  1. 这里着重注意构造函数,参数列表相关问题

  2. 如果定义了其他的构造函数,最好也提供一个默认构造函数(因为编译器不会产生)

    class NoDefault
    {
    public:
        NoDefault(const string &);
        //定义了构造函数,但没有默认构造函数,而且编译器不会生成,这样这个类本身不会有问题,但是用起来很容易出问题
    };
    
    struct A
    {
        NoDefault a;
    };
    A a; //你这样看着挺正常,A没有写构造函数,于是编译器会自动生成默认构造函数去初始化成员变量,可惜啊,这个成员变量的
    //类型是类类型NoDefault,而这个该死的类类型又没有默认构造函数,还不让编译器自己生成,于是就报错了
    
    //还有一种错误是这样的:
    struct B
    {
        B(){}
        NoDefault b;
        //b作为类类型成员,在构造函数没有被初始化,于是它会调用默认构造函数初始化,结果该死的NoDefault没有默认构造函数
    };
    

    注意,没有默认构造函数的类型,要在初始化列表中初始化

  3. 加explicit关键字就可以阻止隐式转换,注意,explicit关键字只对类内的转换构造函数有用,而且被声明为explicit的构造函数只能用于直接初始化,不能拷贝初始化:

    class Sales_data
    {
    public:
        Sales_data() = default;
        explicit Sales_data(const string &s): bookNo(s){}
        explicit Sales_data(istream&);
    };
    
    Sales_data item;
    string b = "1"
    item.combine(b); //这样就不行了
    explicit Sales_data::Sales_data(istream& is){} //这样也不行,在外面了
    Sales_data item1(b); //这样可以
    Sales_data item2 = item1; //不行,explicit声明的不能拷贝初始化
    
    //但是,多事C++又说我们还是可以强行转换:
    item.combine(Sales_data(b)); //可以
    item.combine(static_cats<Sales_data>(cin)); //可以
    

第八章 IO库

IO类

  1. IO处理类型
    1. iostream定义了用于读写流的基本类型
    2. fstream定义了读写命名文件的类型
    3. sstream定义了读写内存string对象的类型
  2. IO对象不能拷贝或赋值

文件输入输出

  1. 头文件fstream定义了三个类型来支持文件IO:

    1. ifstream:从一个给定文件读取数据
    2. ofstream:向一个给定文件写入数据
    3. fstream:读写给定文件
  2. 使用文件流对象

    ifstream input(record); //打开销售记录文件,关联到input
    ofstream output(out); //打开输出文件
    
    Sales_data total; //保存销售额总量
    if(read(input, total)) //读取第一条销售记录
    {
        Sales_data trans; //保存下一条
        while(read(input, trans))
        {
            if(total.isbn() == trans.isbn())
            {
                total.combine(trans); //同一条,加上去
            }
            else
            {
                print(output, total) << endl; //不同的,输出前一条,刷新缓存
                total = trans; //准备搞下一条
            }
        }
        print(output, total) << endl; //打印最后一条
    }
    else
    {
        cerr << "没数据啊老板" << endl;
    }
    
  3. 成员函数open和close

    string ifile = "f";
    ifstream in(ifile); //构建一个ifstream并打开f文件
    ofstream out;
    out.open(ifile + "1"); //打开指定文件f1,out与f1关联
    if(out) //如果打开成功(文件不能被连续打开)
    {
        in.close(); //关闭文件f
        in.open(ifile + "2"); //打开f2文件
    }
    else
    {
        cerr << "文件不存在" << endl;
    }
    

参考

  1. C++ Primer 第五版
  2. C++ Primer
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值