《C++ primer plus》(第六版)进阶学习笔记

置顶

  1. 这本书好乱啊。。。各个知识点零散分布
  2. 以下笔记是本人在有一定基础的情况下,根据自己的不足或者想复习一下而做的。像一些基础的if else都省略了,像常量指针,指针常量这些问题本人已经基本掌握,所以也没记。
  3. 看完之后,回顾一下,仍觉得有很多地方不甚理解,比如虚函数,懂虚表什么的,但仍觉的还有许多要注意的。这个只能在具体的项目中去实践了。还有这只是C++的语法细节,还有很多库的使用什么的,诶。

第三章:处理数据

  1. C++新增整型:unsigned long long, long long 至少64位
  2. using std::cout
  3. char类型
  • wchar_t: 处理超过8位的字符集,如日文汉子系统。cin/cout处理char流,不适合wchar_t,
    可以用wcin/wcout。用L表示
    wchar_t a = L'c';
  • char16_t/char32_t: C++11新增类型,wchar_t的长度和特征随着实现而改变,不适合unicode编码,因此新增这两种类型
    两种都是无符号的,分别用u/U表示
    char16_t c16=u'a';
    char32_t c16=U'\U0000222B';
    U"hello";   //字符串常量
  1. C++11的auto
  • 和C中的auto不同,作用是编译器根据初始值自动推断数据类型
  • 处理简单类型不必要,用处一般是较复杂的类型,比如STL中的迭代器自动推断。
    std::vector<int> test;
    auto p=test.begin();

第四章:复合类型

  1. 数组,char[]字符串,注意若要处理输入,注意\n是否在缓冲区内等等问题,看输入是否符合预期
  2. string(C++98标准)
  3. 结构struct,枚举enum,共同体union
  4. 指针数组,new int[10] 用delete[]释放,new int用delete释放
  5. 自动存储,静态存储,动态存储。实际上就是局部变量存在栈区,static存在数据区,new的存在堆区
  6. 数组的替代——vector,vector效率比[]低,但更安全,若想效率高点,可以用array<int,5> a;表示5个int类型的数组

第五章:循环和关系表达式

  1. 基于范围的for循环(C++11)
    double prices[5]={1,2,3,4,5};
    for(double x:prices){
        cout<<x<<endl;
    }
    for(double &x:prices){
        x=1;//这是要修改prices里面的值的话,要加&
    }
  1. 其他的for(;?{}, while(){}, do{}while(), 没什么说的

第六章:分支语句与逻辑运算符


  1. 从C继承的(ctype.h)与字符相关的库
函数名称作用与返回值
isalnum©若c是字母或数字,返回true
其他的需要时自己搜吧,知道有这个功能就ok了
toupper©返回c的大写,不改变c
tolower©返回c的小写,不改变c

第七、八章:函数

  1. 函数指针,回调函数。可以用typedef简化函数声明
  2. 内联函数inline,可以替代#define
  3. 引用传值,默认参数
  4. 函数重载,参数可以数量和类型不同。只有返回值类型不同不算重载
  5. 函数模板,可以进行模板函数的重载,只要参数不一样就行
    template<class T,class T2>
    T test(T a,T b){

    }
  1. 函数模板具体化
    为了解决某些特殊类型的问题,比如结构体不能加减,可以单独写一个针对此结构体的类型函数,如下所示
    template<> void test<Student>(Student,Student);
  1. 函数模板实例化
    • 实例化和具体化不一样,实例化分为隐士和显示实例化,显示实例化如下,两者的书写格式主要是少个<>
    • 实例化我理解是指定生成这种类型的函数,如下,生成一个参数为int的函数。不然的话得根据参数自己去生成
    • 具体化是在声明的时候用的,实例化实在代码执行中用的
    template void test<int>(int,int);
  1. 通过使用auto和decltype关键字,可以在模板函数中推导不确定的类型,这个需要的时候再查
    template<class T1,class T2>
    auto gt(T1 x,T2 y) -> decltype(x+y){
        ...
        return x+y;
    }

第九章:内存模型和名称空间

  1. 存储持续性
    • 自动
    • 静态
    • 动态
    • 线程存储持续性(C++11),上三个和C一样,这个是和线程声明周期一样长的,用thread_local声明
  2. 作用域和链接性都和C差不多,已经做过笔记不再赘述
  3. mutable 关键字声明的变量,即使加const也可以修改,用在结构体中
    struct Student{
        int id;
        mutable string name;
    };
    const Student s;
    //s的id不能改,但是名字能改
  1. 函数链接性
    默认是外部链接
    加上static后就变为内部链接
  2. 名称空间
    namespace lvkou{
        int id;
        bool sex;
    }
  1. using使用
    • using声明 lvkou::id=1;
    • using编译 using namespace lvkou;
    • using替代typedef using xx=vector;
  2. 匿名名称空间
    namespace{
        int aa;
    }
    //等价于,即将作用域限制与本文件
    static int aa;

第十章:对象和类

  1. 默认构造
    • 不写构造函数的话自动生成默认构造
    • 使用:Student s=new Student; //不加小括号代表使用默认构造------加小括号:Student()
    • 只有一个参数的构造函数,接受参数类型到类类型的隐士转换
    • 用explicit声明的构造函数,不能用于隐式转换
  2. 析构函数
    • 若有堆空间,需要自己写析构函数释放,否则使用默认的析构函数就行
    • 若是虚基类,一定要把析构函数声明为virtual
  3. 构造函数
    • 写在函数里的是赋值,不是初始化,写在函数列表后面的是初始化,初始化顺序和声明顺序一样,和写的顺序无关
  4. private,protect,public的用法区别得会
  5. const成员函数
    声明为const的函数,只能读取类成员,不能改变类成员,若在类使用过程中定义了一个const的对象,则不能调用非const的成员函数
    void show() const{
        ...
    }
  1. this的使用
    • 一般在返回的时候使用,return *this,例如操作符重载
  2. const成员变量
    • 在类内声明const, 同时要加上static

第十一、十二章:使用类

  1. 运算符重载
    • ++的两个重载注意一下,参数加int的是 ++i, 不加int的是i++
    • 仿函数:重载()运算符
    • 除了()、[]、->、=这四种不能重载为非成员函数外,其他函数都能写为非成员函数。
    • 根据类设计,如果不是必须,把操作符重载写为非成员函数更好一些,尤其是为类定义类型转换时
    Student operator=(const Student&){

        return *this;
    }
  1. 友元
    • 友元函数
    • 友元类
    • 友元成员函数
    //Time的hours是private的, 令该函数为Time类的友元就能访问了
    void operator<<(ostream& os,const Time& t){
        os<<t.hours<<":"<<"t.minutes";
    }
    //但上面这种情况不能处理 cout<<a<<b; 这种连续输出的情况
    //因为上面相当于(cout<<a)<<b;   而(cout<<a)返回的不是ostream类型,修改如下:
    ostream& operator<<(ostream& os,const Time& t){
        os<<t.hours<<":"<<"t.minutes";
        return os;
    }
  1. 拷贝构造/赋值构造
    • 若有成员变量在堆区,要自己写拷贝构造函数进行深拷贝,否则只是浅拷贝
    • 赋值构造是重写=操作符,和拷贝构造类似,不过应该返回引用类型
Student::Student(Student& s){
    ...
}
Student& operator=(const Student& s){
    ...
    return *this;
}
  1. 调用拷贝构造的三种情况
    • 使用
    • 对象参数
    • 返回对象
  2. 析构函数的调用情况
    • 局部,区域结束调用
    • static,程序结束调用
    • new, 手动delete,否则等到程序结束,由操作系统释放
  3. 定位new
    我去还有这种操作,,,第一次学
    就是指定new 后对象放置的空间
char* buf=new char[100];
Student* s1,*s2;
s1=new(buf) Student;
s2=new(buf) Student;
//s2把s1覆盖了
  1. C++11允许类内初始化,相当于默认值

第十三章:类继承

  1. 继承is-a, 包含has-a
  2. 三种继承, 只有public能被对象调用
基类访问特性继承方法子类访问特性
publicpublicpublic
protect-protect
private-no acess
publicprotectprotect
protect-protect
private-no acess
publicprivateno acess
protect-no acess
private-no acess
无语,Markdown不支持单元格合并,写html又觉得太麻烦了
  1. 构造
    • 派生类先调用基类构造函数,再调用自己的构造函数
    • 若基类构造函数需要参数,在构造函数的初始化列表处传过去
  2. 多态
    • 静态多态:重写基类方法,注意重写和重载的区别
    • 动态多态:基类使用虚方法,注意若有虚方法,基类的析构函数最好也声明为virtual
    - 为什么声明为虚析构?
        因为若不声明,则派生类对象结束后会调用基类的析构方法,而不会调用自己的,可能造成内存泄露
    - 为什么叫动态?
        若基类存在虚方法,则会对其对象添加一个隐藏成员,保存一个指向函数地址数组的指针,这种数组叫虚函数表,表项为函数地址。在创建对象时,也继承虚表,若是派生类对象,则修改对应函数的地址,这样会额外占些空间,且程序速度降低
    - 虚方法在派生类中不一定非要重写,虚函数表项保留基类函数地址,但不重写的话也没必要定义为派生类
    - 构造函数不能是虚函数
    - 如果重定义时和基类的参数不符合,可能会有警告。会覆盖基类的这个函数,不是重载
  1. 静态联编和动态联编
    • 动态联编,还是有虚继承的时候,用指针运行时才确定对象类型
    • 静态联编,不用指针,直接使用对象,编译期间确定对象类型
    Base *b= new Son;//动态
    Base b; //确定为Base基类类型了
  1. 抽象基类
    • 纯虚方法,也叫接口,只定义,在子类中必须重写
    • 顶基类一般全部定义为接口
    virtual void eat3()=0;

第十四章:C++中的代码重用

  1. 通常,包含,私有继承,公有继承用于实现has-a的关系
  2. 最好使用包含,除非要重新定义虚函数,或者要用到基类的protect成员,否则用包含,容易理解
  3. 多重继承,典型的菱形继承问题,可以用虚基类,xxx:virtual public name{};
  4. 类模板 template
  5. 模板的具体化:隐式实例化,显式实例化,显式具体化。上面说过,不再赘述
  6. 模板别名 using aaa=std::vector
  7. 模板友元,非模板 友元,约束模板友元,非约束模板友元。这个需要的时候再细看。

第十五章:友元,异常和其他

  1. 友元类的使用
  2. 嵌套类的使用
  3. 异常处理
    • abord(),stdlib.h库,直接退出程序,需要程序员自己判断什么时候调用
    • 返回错误码,在函数参数用用一个参数传指针或引用来存错误码,还是需要程序员自己判断
        bool test(int a,int* ERR_CODE){
            ......
        }
    
    • 异常机制
        //捕获异常
        try{
    
        }catch(char* s){
            cout<<s<<endl;
        }
        //抛出异常
        if(a==0){
            throw "a is 0"
        }
    
  4. return 和 throw的不同处理方式
    • return返回上一个调用此函数的函数
    • throw一直往上找到catch的语句,没有就abort()
  5. 基类引用可以执行派生类对象,这个在多重继承中有用,throw()参数若是对象的话,不能先捕获基类引用
  6. exception类,所有异常类的基类
        try{
            ......
        }catch(exception& e){
            cout<<e.what()<<endl;   //输入错误信息
        }
    
  7. new 的异常处理
        //new 失败时返回null
       int* a=new(std::nothrow) int;    
    
  8. RTTI 运行阶段类型识别(Runtime Type Identification)
  • 注意:RTTI只适用于包含虚函数的类
  • 三个支持RTTI的元素
    • dynamic_cast 运算符使用一个指向基类的指针来生成一个指向派生类的指针
    • typeid,返回一个指出对象类型的值
    • type_info,结构存储有关特定类型的值
  • dynamic_cast
    • 它主要是确保类型转换的安全性
Base *b=new Base;
Person *p=new Person;
Student *s=new Student; //注意这三者是依次继承的
Student *p1=(Student*)s;    //安全的
Student *p2=(Student*)b;    //不安全
Person* p3=(Student*)s;     //安全
//用dynamic_cast转换后,不安全的就会返回null
Person* s=dynamic_cast<Person*>(b)
//指针b指向的类型是否可以转换为Person*类型,若果可以转换成功,不行的话返回null
  • typeid(obj)==typeid(obj2),通过这种方式判断是不是同一个类,typeid返回一个type_info类型,能获得对象信息
  1. 更严格的类型转换
  • dynamic_cast
  • const_cast 把const改为非const
  • static_cast
  • reinterpret_cast

第十六章:string类和标准模板库

  1. string的几种构造函数
    • 字符串
    • 迭代器
    • n个字符
    • 初始化列表(C++11)
    • 截取字符串
  2. find的几种用法
  3. size(),capacity(),reserve()的区别
    • reserve()是请求最小内存大小
    • capacity()是返回总内存大小
    • size()是使用的元素个数
  4. 智能指针
    • auto_ptr 已弃用,因为可能会有问题,例如把指针赋值给另一个指针后,上一个指针就失效了,但还存在
        //例
        unique<Student> p=new Student;
        unique<Student> q;
        q=p;    //这个赋值会让p失效,万一以后使用p的话就会出错
            //为什么让p失效?为了防止以后对同一对象调用两次析构函数
                //用unique_ptr的话这里编译不通过,若想赋值的话得用move()
        q=move(p);
    
    • unique_ptr auto_ptr的替代品
    • share_ptr 能多个指针指向同一对象
        unique<Student> p=new Student;
    
    • 注意,auto_ptr和share_ptr只适用于new分配的内存,不适用于new[]。unique两种都适用
  5. vector的使用
    • 增删改查
    • for_each(v.begin(),v.end(),dosomething); 对每个元素执行dosomething操作
    • random_shuffle(v.begin(),v.end()); 对v中元素打乱排序
    • sort(v.begin(),v.end(),compare); 快排,默认从小到大
    • 基于范围的for循环
        for(auto &x:v){
            ......
        }
    
    • 注意迭代器的本质是指针,某些地方可以用指针代替迭代器,注意是某些地方
    • cbegin()获得const迭代器,不能修改指向的值
    • rbegin()等于end(),但二者类型不同
  6. 迭代器类型
  • 输入迭代器
  • 输出迭代器
    //这个可以用于输出vector中的元素,等价于把
    copy(v.begin(),v.end(),ostream_iterator<int>(cout," ))
    //把v中的元素拷贝到输出迭代器,等价于对每个元素执行 cout<<v[i]<<" ";
  • 正向迭代器
  • 双向迭代器
  • 随即访问迭代器
  1. 容器类型
  • 序列容器
    • vector
    • deque
    • list 双向链表,容器插入或删除后,迭代器指向元素不变
    • forward_list(C++11) 单链表,功能没有list强,但简单紧凑
    • queue
    • priority_queue 默认底层类是vector,总把最大元素放到队首,也可以自定义首部元素规则
    • stack
    • array(C++11)
  • 关联容器
    • set
    • multiset
    • map
    • multimap
  • 无序关联容器
    关联容器底层实现是红黑树,无序关联容器底层实现是哈希表
    • unodered_set
    • unordered_multiset
    • unorder_map
    • unordered_multimap
  1. 函数对象(仿函数)
    • transform(v.begin(),v.end(),v2.begin(),out,plus<>()) //更多transform学STL时再细写
    • 看了一些例子,觉得transform用熟练了,无敌啊
  2. 算法组
  • 非修改式序列操作 如find,for_each等
  • 修改式序列操作 如transform,random_shuffle,copy等
  • 排序和相关操作 如sort等
  • 通用数字运算 前三个在,这个在
  1. 其他库
  • valarray库
    • 能方便的对数组内的数值进行计算,指出sum(),max(),min()等的计算。
    • 没有.begin() .end()函数,想用可以 sort(begin(obg),end(obj));
    • 用vectoc存数据,复制到valarray里面进行计算
  1. 模板initializer_list(C++11)

第十七章:输入输出和文件

  1. 输出格式控制:cout.width(5)等,但感觉不如printf方便好记
  2. iomanip,专门控制输出格式的头文件,常用的有setw(),setprecision(),setfill()
  3. 文件io
    ofstream fout;
    fout.open("xxx.txt"); 
    fout<<"hello word!";
    fout.close();
    string rel;
    ifstream fin;
    fin.open("xxx.txt");
    fin>>rel;
    fin.close();
  1. 流状态检查
    if(!fin.is_open()){
        ......//打开失败
    }
  1. 文件模式
常量含义
ios_base::in读文件
ios_base::out写文件
ios_base::ate打开文件并移到文件尾
ios_base::app追加到文件尾
ios_base::trunc如果文件存在,则截断文件
ios_base::binary二进制文件
输入流的默认为ios_base::in;
输出流的默认为ios_base::out|ios_base::trunc
  1. 讲真C++文件操作看着真费劲,不如fread,fwrite或者read,write

第十八章:C++新标准

  1. 复习前面介绍过的C++11
  • 新类型 long long ,unsigned long long
  • 统一的初始化,可以用{1,2,3}列表初始化,
    • 窄缩,禁止将数值赋给无法存储它的数据类型
    • std::initializer_list
  • 声明
    • auto
    • decltype(x) y,将y声明为和x一个类型,在定义模板时很有用
        template<class T,class U>
        auto test(T t,U u) -> decltype(T*U){
    
        }
    
    • 返回类型后置,如上代码例子,专门用于模板这种
    • 模板别名 using xx=xxxx; 代替typedef
    • nullptr,C++11的空指针
  • 智能指针 unique_ptr,shared_ptr,weak_ptr
  • 异常规范的修改
    • 不能再指明函数可能引发的异常
        void test() throw(bad_exp){
    
        }//错误
    
    • 可以声明不会产生异常
        void test() noexcept{
    
        }
    
  • 作用域内枚举
enum{a,b,c}     //traditional form
enum class a_new{a,b,c} //new
enum struce b_new{a,b,c}    //new
//可以根据类名来选择枚举名,以前不能用重名,现在可以了
  • 对类的修改
    • explicit //对构造函数声明,禁止隐式转换
    • 类内成员初始化,可以在创建类的时候初始化成员,当做默认值
  • 模板和STL方面的修改
    • 基于范围的for循环 for(auto x:v){…}
    • 新增的STL容器 forward_list,unordered_系列,array
    • 新的STL方法 cbegin(),cend(),const迭代器
    • valarray升级,新加了begin(),end(),使得STL算法可以应用在上面
    • 摒弃export
    • 尖括号
        std::vector<<vector<int> > //以前为了怕识别成>>,后面要用空格隔开,C++11不用了
        std::vector<<vector<int>> //C++11这样写也行
    
  • 右值引用
    && 声明,引用等号右边的值
        int &&a=x+y;
        //a为x+y的结果,以后x+y改变,a也不会变
    
  1. 移动构造和右值引用
  • 主要是利用右值引用写构造函数和赋值函数
  • 对程序员来说重要的不是写右值引用的代码,而是使用利用右值引用实现移动语义库的代码
  1. 新的类功能
  • 特殊的成员函数,在原有的四个默认情况下新增了两个
    • 移动构造函数
    • 移动赋值函数
      原来的四个是:
    • 默认构造
    • 默认拷贝构造
    • 默认赋值
    • 默认析构
  • 默认的方法和禁用的方法
    • Student() =default; //声明默认构造
    • Student(const Student&) =delete //禁止拷贝构造,也可以设置成私有,但不如这个易理解
    • 委托构造,构造中用构造
    • 继承构造函数,就是如果自己没写,就调用基类的相应构造方法,不过派生类的成员变量要通过初始化列表自己初始化
        Student(int id,string name,bool boy):b(boy),Base(id,name){
            ......
        }
    
  • 管理虚方法:override,final
    要解决的问题:如果派生类重写了基类的虚方法,但是参数不同,这样不会重载,是直接覆盖,基类的那个就不能用了,为了解决这个问题,引入override。
    而final是禁止派生类重写这个虚方法
    • override是在派生类中使用的
        //在派生类中
        virtual void test() override{
            ......
        }
    
    • final是在基类中使用的
        virtual void test() final{
            ......
        }
    
  • Lambda函数
    • 格式
        []() ->double{...}
        //返回double的lambda,也可以不声明返回类型,由decltype自动推断
    
    • 用于函数对象,函数指针的地方,如STL算法
    • 为什么用lambda
        STL中常用为比较表达式替代函数对象
        [=],[&] 分别传值和传引用访问所有自动变量
        [xx]传值使用xx变量,加&传引用
    
  • 包装器/适配器
  • 可变参数模板
    template<class T,class... Args>
    void show_list(const T& value,Args&... args){
        //每次只取args里的一个值
        cout<<value<<", ";
        show_list(args...);//这样递归盗用
    }
    template<class T>
    void show_list(const T& value){
        //参数只有一个值的情况下,结束递归
        cout<<value;
    }
  • 并行编程
    • thread_local把变量声明为和线程生命周期一样长
    • 线程支持库:thread,mutex,condition_variable,future
  • 新增的库
    • tuple:扩展的pair,可以存储多个类型不同的值
    • regex:正则表达式库
    • chrono:处理时间间隔
    • ratio:有理算数库,对有理数的算术运算
  • 低级编程
    • 不是质量低级,而是更底层。
    • constexpr 机制让编译器能够在编译阶段计算结果为常量的表达式
  • 杂项
    • assert,static_assert
    • 元编程加强
  • 语言变化
    • boost,tr1,标准库的实验场
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值