Skip to content

Latest commit

 

History

History
214 lines (157 loc) · 11.7 KB

c++ primer.md

File metadata and controls

214 lines (157 loc) · 11.7 KB

C++ Primer

Chapter 1 变量


基本类型

  1. 算术类型和空类型

    算数类型

    类型 大小 精度
    bool 未定义 true / false
    int 4bytes
    short 不大于int
    long 不小于int
    long long 通常8bytes
    float 4bytes 至少6,通常7位小数
    double 8bytes 至少10, 通常16位小数
    char 1byte
    wchar_t 64位ubuntu上4byte 宽字符, 根据机器和系统,
    用于确保可以存放机器最大扩
    展字符集中的任意字符
    空类型: void

    Tips:

    • signed&unsigned 两者在bit级别没有差异,差异在于读取时的理解 重点注意unsigned和signed混用进行运算时, signed会首先隐式转换为unsigned在进行运算,这种情况应当避免.
    • 在算术表达式中避免使用char类型,不同机器的实现可能是signed也可能是unsigned, 如果一定要使用一个不大的整数, 可以使用signed char.
    • 内置类型的变量, 作为全局变量时默认初始化为0, 作为局部变量时, 未定义
    • 所有变量可以用extern声明, 声明后变量名可用,并且类型按照声明的类型. 定义时才真正分配空间, 在单文件的情况下不存在只声明的情况, 在单文件程序中, 用extern int i = 5;其实是定义了i, 并且不能用在函数内部
  2. 类型转换

    • signed和unsigned的转换: 底层bit不变, 解释发生变化
    • 整型到浮点数的转换: 不发生改变, 如果整数所占空间超过了浮点数容量, 精度损失
    • 浮点数到整型的转换: 结果值保留小数点前, 做近似处理,向0取整 (等价于直接截断)
    • 短长度类型到长长度类型的转换: 不发生改变
    • 长长度类型到短长度类型的转化: 如果是赋给无符号类型, 结果是初始值对无符号类型表示数值总数取模后的余数. 例如unsigned char的范围0-255, 将-1赋给它, 得到255; 赋给带符号类型时, 结果是未定义的
    • 当一个算术表达式中既有无符号数又有有符号数时, 有符号值会转换成无符号值
  3. 定义与声明

    • 函数的声明: 函数的声明即是原型, 形参可以不给标识符但是必须给出类型
    • 函数的定义: 函数的定义带有具体的函数体
    • 变量的声明: 变量的声明使用extern关键字
    • 变量的定义: 变量的定义只能够在所有文件中出现一次, 变量在定义时分配空间, 能够跨文件出现的变量通常位于内存的数据区, 可能是.data 段也可能是.bss段(为定义初始值的全局变量的位置)
    • 在同一条语句中,可以用先定义的变量初始化后定义的其他变量。
    • 初始化不是复制,初始化的含义是创建变量时赋予其一个初始值,赋值的含义是把对象的当前值擦除,以一个新值代替

    Tips:

    • 函数的声明后面需要接分号, 函数的定义后面不能接
    • extern语句如果包含初始值就是一个定义而非声明
    • 在函数体内如果试图初始化一个由extern关键字标记的变量,将引发错误
  4. 作用域

  • 内层作用域可以访问外层作用域的变量
  • 内层可以定义和外层同名的变量,覆盖外层变量
  1. 初始化

在C++中,以下几种初始化都是正确的,

int units_sold = 0;
int units_sold = {0};
int units_sold{0};
int units_sold(0);

列表初始化:用花括号初始化变量的形式,初始化对象和为对象赋新值都可以使用这样一组花括号扩起来的初始值。

当列表初始化用于内置类型时,如果初始值存在丢失信息的风险,编译器会报错。

定义在函数体内的内置类型的对象,如果没有初始化,则值未定义。因此建议初始化没哟个内置类型的变量,虽然并非必须这么做。

复合类型

一条声明语句由一个基本数据类型和紧随其后的一个声明符列表组成。每个声明符命名了一个变量并指定该变量为与基本数据类型有关的某种类型:无() - 变量,&() - 引用,*() - 指针

  • 引用在使用上约等于常指针

const限定符

const变量默认的作用范围是文件内, 编译阶段编译器拿着值替换文件内的该变量. 不同文件的同名const变量相互独立, 如果需要在不同文件中共享同一个const变量, 需要在定义和声明时都加上extern.

顶层const&底层const

  • 顶层const指的是指针本身是const, 底层const表示指针所指的对象是一个常量.
  • 顶层const的写法在右边 int *const pi = &i. 底层const的写法在左边, const int * pi = ci;

常量表达式指的是, 值不会改变并且在编译过程就能得到计算结果的表达式.

constexpr: 在一个复杂系统中, 很难分辨一个初始值到底是不是常量表达式, c++11允许将变量声明为constexpr类型, 由编译器来验证变量的值是否是一个常量表达式. 声明为constexpr的变量一定是个一个常量, 而且必须用常量表达式初始化.

算术类型, 引用和指针都属于字面量类型, 自定义类, IO库, string类型都不属于字面量类型, 都不能被定义为constexpr.

尽管指针和引用类型可以被定义为constexpr, 但是它们的右值非常严格, 只能是0, nullptr或存储在某个固定地址中的对象.

constexpr修饰的指针, 限定符对指针有效, 即constexpr修饰的一定是一个顶层const. 以下定义的是一个指向整数的常量指针.constexpr int *q = nullptr, 相对应的, 这样的const定义的是一个指向整型常量的指针const int *p = nullptr.

auto&decltype

Tips

  • 标识符, 不允许以两个连续的下划线, 不允许"_ + 大写字母", 不允许函数以下划线开头
  • 引用和指针的区别: 都占用了一个地址的空间内存, 在汇编层面相同, 但引用不是对象, 指针是
  • 尽量使用nullptr代替NULL, 尽管目前没有区别
  • void* 类的指针可以赋值, 比较, 但不能使用其指向的对象, 因为无法预知内存长度
  • 对于 int* p, q; * 只修饰了p, 而非整行. 因此 *p, *q的紧邻写法更好
  • 面对指针或引用的复杂声明时, 采用从右向左读的方式理解, int *&r = b的含义, &r--引用, *--指针引用, int *整型指针
  • 指针指向的对象可以被引用绑定int &i = *p;。即引用可以指向指针解引用的对象,但不能定义指向引用的指针
  • 建议初始化所有指针

Chapter 2 表达式


  • 当一个对象被用作右值时, 用的是它的内容, 当一个变量作为左值时, 用的是它的身份(内存位置)
  • 一个左值可以被当做右值使用, 一个右值不能被当做左值使用
  • 对于没有制定运算顺序的运算, 如果表达式指向并修改了同一个对象, 其结果是未定义的
  • 对于一个表达式中的不同函数的运算顺序, 很多时候是不确定的
  • 比较运算符的优先级高于相等运算符
  • 赋值优先级低于关系运算符
  • 后置递增的性能不如前置递增
  • 条件运算符的表达式要求类型一致或至少可以转换为同一类型
  • sizeof 有两种用法, sizeof(type)和sizeof expr, 前者可以用于类, 后者用于对象(实例)
  • 当sizeof作用于解引用的指针时, 即使指针为空也是安全的, 不会真访问值
  • sizeof 作用域数组名 不会自动转换为指针
  • 如果运算对象一个是无符号类型一个是有符号类型, 并且无符号类型不小于有符号类型的话, 有符号类型转换为无符号类型.
  • 使用新式的强制转换 cast_name<type> (expr), static_cast, const_cast, reinterpret_cast

Chapter 3 函数


  • 可以设置未命名的形参, 调用时需要给初始值, 但不会被函数用到
  • 形参仅从T类变为const T类, 不是合法的重载, 原因是初始化时, 顶层const被忽略
  • 对于只读不修改的形参, 应定义为常量引用, 否则会限制实参类型, 使const对象, 字面量和需要进行类型转换的对象不能传入.
  • 返回数组的指针或引用的函数声明的四种写法
  • 不同作用域声明的同名函数不会被重载, 外层函数直接被隐藏.
  • 默认实参可以是函数返回值或变量, 并且对变量的修改会被更新, 因为默认实参的求值发生在调用时.
  • 规模小, 流程直接, 调用频繁的函数, 可以声明成内联, 递归和大于75行的函数不可以声明为内联, 声明为内联只是请求, 不是保证.
  • NDEBUG预处理变量和四个调试用变量和assert可以一起使用.

Chapter 4 类

  • 使用struct和class定义类的唯一区别就是默认的访问权限.
  • 定义在类内部的函数, 是隐式的inline函数
  • 常量成员函数是在形参列表之后加一个const, 含义是函数所用的this指针指向的const的本类对象.
  • 常量对象的指针和引用, 只能调用常量函数
  • 编译器对类的处理分两步, 首先编译成员的声明, 再编译成员函数的, 因此成员变量可以先使用后定义.

Chapter 5 STL


顺序容器

  • vector 可变大小的数组,随机访问,尾部以外的位置插入删除元素很慢
  • list 双向链表,双向顺序访问,头尾插入删除速度快
  • deque 双端队列,随机访问,头尾插入/删除速度快
  • forward_list 单向列表,单向顺序访问,任何位置插入/删除速度快
  • array 固定大小的数组,不能添加删除元素
  • string 与vector相似,尾部插入/删除速度快

Tips:

  • 如果在读取输入时,需要向中间插入元素,之后需要随机访问,可以在输入阶段使用list,之后拷贝到vector当中进行查找
  • 顺序容器的一个版本的构造函数接受容器的大小参数,它使用了元素类型的默认构造函数,但某些类没有默认构造函数,这时,我们可以定义保存这种类型的对象的容器,但是不能在构造这种容器时只传递给它一个元素的数目参数。

顺序容器的操作

  • 迭代定义了递增减,但forward-list的没有定义--
  • 向vector,deque,string插入元素会使所指向的容器的引用,指针,迭代器失效
  • 删除deque首尾以外的元素会使指向其的所有指针,引用,迭代器失效,删除vector的stag元素,会使删除点之后的的所有引用,指针,迭代器失效

vector对象的增长

容器适配器

关联容器

关联容器主要包括map, set, multimap, multiset以及上述四种容器的unodered版本, 搭配使用的还有pair以及make_pair函数.

关联容器的数据类型别名 key_type: 键对应的数据类型, 对于set就是元素自身 maped_type: 值类型, 对于set就是元素自身 value_type: set就是其自身元素的类型;map是pair<key_type, map_type>类型

关联容器的插入操作

关联容器的删除操作

关联容器的更改操作

关联容器的查询

chapter 6 模板与泛型

chapter 7 内存管理

chapter 8 其他