我学 C++ 编程
- 入门
- 命名空间
- 解决命名冲突
- 库里的代码都在std命名空间
- namespace()
- 普通定义
- 嵌套
- 命名空间可以合并
- 使用
- 命名空间::成员
- using 命名空间::成员
- using namespace 命名空间:不建议使用,会导致命名空间污染,导致多个命名空间中的变量命名冲突
- 函数重载
- 函数名相同,参数不同
- 个数不同
- 参数类型不同
- 顺序不同
- 返回值不能作为函数重载的标记
- C语言不支持函数重载
- 函数名修饰规则:name maniging, _函数名
- C++支持
- 函数名修饰规则:函数名+参数,不同的版本实现不一样,Linux:_Z+函数名+参数,VS:@类名,命名空间@函数名@参数乐行:YZH@
- 函数名相同,参数不同
- 引用
- 语法:一个变量的别名,不分配空间
- 底层实现:与指针的实现方法相同
- 做函数的参数或者返回值,效率比传值高
- 函数返回值:返回变量的声明周期至少大于调用函数的声明周期
- 引用和指针的区别
- 引用在定义时必须初始化,指针可以不初始化
- 有多级指针,没有多级引用
- 有空指针,没有空引用
- sizeof(引用):变量的大小,size(指针):指针大小
- ++:引用++时变量本身的值++,指针++是指针向后便宜一个类型的地址
- inline
- inline函数名
- 一般会在调用的地方展开
- 简单的代码编译器直接展开
- 如果较长,或者有循环,递归逻辑,编译器不会展开
- 和宏函数相比:有语法检查,比宏函数安全,一般会用内联函数代替宏函数
- 不支持分离编译,声明和定义放在一起
- C++11
- auto:类型的占位符
- 定义auto变量,需要有初始化表达式,编译器通过初始化表达式推导变量类型
- 不能使用auto的地方
- 不能作为函数形参
- 不能定义数组
- 不能定义类的非静态成员变量
- 不能作为模板的参数
- auto:类型的占位符
- 命名空间
- 类和对象
- 什么是类
- 把数据和函数打包定义在一起的结构
- 定义的类不占空间
- 可以看做一个设计图纸
- 类的实例化
- 定义一个类类型的变量
- 对象模型
- 只存放普通成员变量
- 大小遵循内存对齐的原则
- 空类对象占1一个字节
- 类的访问和权限关键字
- 公有
- public
- 在类外直接访问
- public
- 私有
- private
- 在类外不能访问
- protect
- 在类外不能访问
- private
- 关键字在类内部不起作用,相对于类外成员的访问限制,类内部所有成员可以直接访问
- 公有
- this指针
- 只存在于成员函数内部,势函数的第一个参数
- 类类型 *const
- 始终指向当前调用函数的对象
- 6大成员函数
- 构造函数
- 函数名和类型相同,无返回值,可以重载
- 初始化列表
- 成员变量定义的地方
- 成员变量初始化的顺序和定义的顺序相同,与在初始化列表中的顺序无关
- 必须在初始化列表中初始化的成员
- 引用
- const成员
- 没有默认构造的自定义成员
- 默认构造
- 无参构造
- 全缺省构造
- 编译器默认生成的构造
- 默认构造只能存在一个,一般会定义一个全缺省构造
- 如果没有定义构造,编译器会默认生成,如果已定义,编译器不会再生成默认构造
- 构造函数会调用自定义成员的默认构造函数,完成自定义成员的初始化
- 对于内置类型成员,空的构造函数不会初始化
- 构造函数在实例化对象的时候,编译器自动调用
- 拷贝构造
- 函数名和类名相同,参数必须为引用类型,如果传值会造成无穷递归
- 用已有对象创建一个新的对象时,编译器自动调用
- 类型变量(对象),类型 变量 = 对象
- 现代写法
- 在初始化列表中初始化成员
- 函数内部调用构造函数创建一个临时对象
- 用过swap和临时对象交换成员变量
- 析构
- ~类名()
- 完成资源清理工作
- 对象生命周期结束,编译器自动调用
- 不能重载
- 赋值运算符重构函数
- 运算符重载函数
- 函数名:operator+运算符
- 定义方式和普通函数没有区别,有参数,返回值
- 不能重载的运算符:, * ? :: sizeof
- 定义为成员函数时,实际的参数比运算符本身需要的参数个数少一个,第一个传入的参数始终为this指针,编译器自动传入this指针
- 函数名:operator+运算符
- operator=(参数)
- 返回值:引用,返回值用来作连续赋值
- 检查是否自己给自己赋值
- 返回*this
- 现代写法
- 代码复用
- 参数传值,传参时引发拷贝构造,创建一个临时对象
- 使用swap完成和临时对象成员变量的交换
- 临时对象的声明周期结束时,调用析构完成原有空间释放
- 运算符重载函数
- 取地址运算符重载
- 非const函数
- const函数
- 一般不需要重写,直接使用编译器自动生成的
- 函数内部返回:this
- 构造函数
- const
- 成员函数中,const实际上修饰的this指针,成员函数内部不能修改成员变量的值
- const成员函数不能调用非const成员函数
- 非const成员可以调用const成员函数
- const对象不能调用非const成员函数
- 非const对象可以调用所有成员函数
- static成员
- 成员变量存放在静态数据区
- 成员变量必须在类外初始化
- 成员变量全局只有一个,所有对象共享
- 成员函数
- 没有this指针
- 函数内部不能调用非静态成员函数
- 非静态成员函数可以调用静态成员函数
- 友元
- 友元函数
- friend函数()
- 可以突破封装,访问对象的私有成员
- 一般不建议使用
- 重载<<,>>运算符时用友元
- 友元类
- friend class 类名
- 友元关系单向的
- 友元关系不能传递
- 通过对象.访问所有成员
- 内部类
- 具有友元类的所有特性
- 对于外部类的static,枚举可以直接访问
- 是一个独立的类,不从属于外部类,外部类对于内部类没有优越的访问权限
- sizeof(外部类):外部类的大小,不包含内部类
- friend class 类名
- 友元函数
- 什么是类
- 内存管理
- 内存分布
- 系统内核
- 栈
- 函数局部变量
- 堆
- 动态开辟的空间
- 数据段
- 全局数据
- 静态数据
- 代码段
- 可执行代码(机器码)
- 文字常量
- C语言内存管理
- malloc
- calloc
- 申请空间,按字节初始化为0
- realloc
- 原始空间如果小于重新申请的空间
- 如果原始空间尾部有赋予的空间满足,直接修改底层标记
- 否则,重新申请一片更大的空间,拷贝原始空间的内容,释放原有空间
- 否则
- 直接修改底层标记
- 原始空间如果小于重新申请的空间
- C++内存管理
- new, new[],delete, delete[]
- new
- 内置类型
- operator nw–>malloc+异常
- 自定义类型
- operator new -->malloc+异常–> 构造函数
- operator new VS malloc
- malloc只申请空间,空间申请失败,返回NULL
- operator new空间申请失败,抛异常
- operator new
- 定制自定义类型空间申请的方式,通过重写operator new实现,不影响其它类型的空间申请方式
- 内置类型
- delete
- 内置类型
- operator delete -->free
- 自定义类型
- 析构函数–>operator delete–>free
- operator delete
- 定制自定义类型空间释放的方式,通过重写operator delete实现,不影响其它类型的空间释放方式
- 内置类型
- new定位表达式
- new(类型指针)构造函数(参数)
- 显示调用构造函数,完成与分配空间的内容初始化
- 面试题
- 如何创建只在栈上创建对象的类
- 第一种方式
- 构造函数私有
- 提供公有的静态方法,在方法内部调用够赞函数创建对象
- 第二种方式
- 禁止new,delete关键字的调用
- 只声明不实现 operator new,operator delete
- 或者声明operator new,operator delete为delete函数,C++11方式
- 禁止new,delete关键字的调用
- 第一种方式
- 如何创建能在堆上创建对象的类
- 构造函数私有化
- 提供公有的静态方法,方法内部通过new关键字在堆上创建对象
- 拷贝构造只声明,不实现,或者拷贝构造,构造声明为delete函数(C++11方式)
- 单例模式
- 饿汉模式
- 程序运行之前对象已创建
- 声明一个静态的成员变量,它的类型为当前类本身,在类外初始化,调用构造函数初始化
- 构造函数私有
- 拷贝构造私有,只声明不实现,或者声明为delete函数
- 提供一个公有的静态方法,返回值为指针或者引用
- 优点:简答,没有线程安全问题,多线程对效率要求较高的情况下使用
- 缺点:启动慢,不能定义多个单例的初始化顺序
- 程序运行之前对象已创建
- 懒汉模式
- 在第一次使用对象的时候创建
- 声明一个静态的成员指针,指针类型为当前类本身,在类外初始化为nullptr
- 构造函数私有化
- 拷贝构造私有化,只声明不实现
- 提供一个公有的静态方法,方法内部通过调用new在堆上创建对象,返回指针
- 通过加锁保证线程安全,保证全局只创建一个对象
- double check
- 内部检查是为了保证线程安全
- 外部检查是为了提高效率
- if(_sinptr == nullptr){_mtx.lock();if(_sinptr == nullptr){_sinptr == new 类名()} _mtx.unlock(); } return _sinptr;
- double check
- 优点:启动快,延迟加载,可以定义多个单例的初始化顺序
- 缺点:设计复杂,需要考虑线程安全问题
- 在第一次使用对象的时候创建
- 饿汉模式
- 如何创建只在栈上创建对象的类
- 内存分布
- 模板
- 函数模板
- 模板实例化
- 显示实例化
- 函数名 <类型1, 类型2>
- 隐式实例化
- 函数名,编译器根据输入参数进行类型推演
- 显示实例化
- 非模板函数和模板函数的匹配规则
- 如果实际的参数类型和非模板函数完全匹配,则不再进行模板实例化,直接调用非模板函数
- 如果实际参数类型和非模板函数不完全匹配,此时,模板如果可以生成根据匹配的函数,则进行实例化
- 如果指定显式实例化,无论对应的非模板函数是否存在,都要进行模板函数的实例化过程
- 特化
- 当基础的模板函数实例化的函数不能正确处理某些类型的逻辑,则需要对特定类型定制特化版本
- 如果特化版本实现比较复杂,或者有一些奇怪的编译错误时,建议定义一个普通函数进行处理
- 模板实例化
- 类模板
- 实例化
- 必须显式实例化
- 特化
- 全特化
- template <> class 类名<特化类型1, 特化类型2,…>
- 偏特化
- 部分特化
- template class 类名 <T, 特化类型1,…>
- 对参数作进一步限制
- template <class T1, class T2> class 类名<T1*, T2*>
- 部分特化
- 类型萃取
- 基础类模板和特化版本的结合应用
- 区分内置类型和自定义类型
- struct _trueType{}; struct _falseType{};
- 基础类模板
- template TypeTraits{typedef _falseType _isPodType}
- 内置类型特化
- template <> TypeTraits <内置类型> {typedef _trueType _isPodType};
- 全特化
- 实例化
- 函数模板
- STL
- 容器
- string
- 管理字符顺序表,typedef basic_string string
- 构造,拷贝构造,赋值,析构
- 迭代器
- begin(), cbegin()
- 返回第一个字符所在位置
- rbegin(), crbegin()
- 返回最后一个字符的一个位置
- end(), cend()
- 返回最后一个字符的下一个位置
- rend(), crend()
- 返回第一个字符的前一个位置
- 类似于指针
- 解引用
- 获取字符内容
- ++
- 移动到下一个字符的位置
- –
- 移动到上一个字符的位置
- 其他
- 解引用
- begin(), cbegin()
- 访问
- operator[]
- 可读可写
- 本质上为成员函数char& operator[](size_t pos)的调用
- 迭代器
- 可读可写
- 基于范围for
- 可读可写
- 本质为底层通过迭代器的形式访问
- operator[]
- 增删改查
- +=
- += char
- += 字符串
- += string
- pop_back()
- insert()
- 可以在任意一个位置插入元素
- 效率低,除过尾插,其它位置时间复杂度为O(n)
- erase()
- 可以在任意位置删除
- 效率低,除过尾删,其他位置时间复杂度为O(n)
- find()
- 查找字符在string中的位置,如果找不到,返回npos
- substr()
- 构建子串string
- +=
- 容量
- resize
- 改变有效字符的数量
- 可能会引起增容:当n>size,引起增容
- reserve
- 只增容,不减少容量
- 增容逻辑:初始大小一般为15字节,后续一般是2,1.5增加
- resize
- 拷贝
- 浅拷贝
- 字节序拷贝,值拷贝,补考被自愿,值拷贝对象模型中的内容
- 深拷贝
- 即拷贝对象模型中的内容,如果有资源,一起拷贝
- 写时拷贝
- 浅拷贝+引用次数
- 浅拷贝
- vector
- 管理各种类型的顺序表
- 迭代器
- 失效问题
- 插入
- 增容导致迭代器原来指向的位置已经被释放,无法访问
- 插入操作之后,重新获取迭代器
- 删除
- 迭代器访问越界
- 获取删除借口的返回值,返回值表示的是下一个元素的位置
- 插入
- 失效问题
- 成员变量为三个指针
- T* _start
- 为begin()的返回值
- T* _finish
- 为end()的返回值
- _endOfStorage
- 当前申请的空间的最后一个位置的末尾,通过_endOfStorage -_start表示容量
- T* _start
- 拷贝
- 内置类型
- 通过字节拷贝,即memcpy(),效率高,时间复杂度O(1)
- 自定义类型
- 调用自定义类型的拷贝构造或者复制运算符重载完成深拷贝,效率一般,时间复杂度O(n)
- 内置类型
- list
- 带头循环的双向链表,可以存放各种类型的数据
- 非原生迭代器实现
- 通过封装节点实现迭代器
- 解引用
- T& operator(){return node_data}
- ++,–
- 前置++
- self & operator++(){ node = node -> _next; return *this;}
- 后置++
- self operator++(int)(self tmp(*this); node = node -> _next; return tmp;)
- 前置–
- self&operator–(){node = node->_prev; return *this;}
- 后置–
- self operator–(int){self tmp(*this); node = node ->_prev; return tmp;}
- 前置++
- !=,==
- 直接比较节点的位置是否相同
- ->
- 获取节点中存放数据的成员,T*–>成员
- T* operator->(){return &(_node->_data); 或者 return &operator *{}}
- 迭代器失效
- 删除
- 删除的节点空间被释放,迭代器指向的位置无效
- 获取迭代器返回值,返回值指向下一个节点的位置
- 删除
- list和vector区别
- vector连续空间,list非连续空间
- vector空间利用率高,不会造成内存碎片,list容易造成额你村碎片,空间利用率较低
- 对方问效率要求高,不需要经常进行插入删除操作,vector比较合适,需要频繁的插入删除,对访问效率要求较低的情景,list比较合适
- vector插入和删除都可能导致迭代器失效,list删除会导致迭代器失效
- vector除过尾插尾删,其他位置插入删除的时间复杂度为O(n),list:在任何地方插入和删除的时间复杂度都是O(1)
- vector支持随机访问,list不支持
- vector迭代器实现方式:原生指针,list迭代器实现方式:非原生指针
- deque
- 实现方式:中控+buffer
- 中控
- 指针数组
- buffer
- 实际存放元素的空间
- 每个buffer 的大小定长
- 增容
- buffer
- 开辟空间,把空间的首地址存放在中控的指针数组中
- 中控
- 开辟新的更大的空间,把原有指针数组中的内容通过字节拷贝的方式一次拷贝,释放原有指针数组的空间,容器中存放的元素不需要拷贝
- buffer
- 特点
- 兼容了list和vector部分优势
- 增容代价小
- 支持随机访问
- 空间利用率高,不会造成内存碎片
- 头插、尾插、头删、尾删时间复杂度都为O(1)
- 物理上不连续,逻辑上连续的数据结构
- 迭代器
- start–成员
- T*first–指向buffer的第一个位置
- T*last–指向buffer最后一个位置的末尾
- T*cur指向第一个buffer 的第一个元素的位置
- T**node 指向中控中的第一个位置
- finish–成员
- T*first 指向buffer的第一个位置
- T*last 指向buffer最后一个位置的末尾
- T*cur 指向最后一个buffer的最后一个元素的下一个位置
- T**node 指向中控中的一个位置
- start–成员
- string
- 迭代器
- 正向迭代器
- 反向迭代器
- const迭代器
- 容器适配器
- 适配器:把一种容器,通过封装,转换成另一种容器,本质:实现转换
- stack
- FILO:后进先出
- 底层结构
- deque:默认
- 尾插尾删时间复杂度为O(1)
- 增容代价小
- 不会造成内存碎片
- vector
- list
- deque:默认
- 底层结构需要实现规定的接口
- pop_back
- push_back
- back
- empty
- size
- 不提供迭代器
- queue
- FIFO:先进先出
- 底层结构
- deque:默认
- list
- 底层结构需要实现规定的接口
- pop_back
- push_back
- back
- empty
- size
- 不提供迭代器
- priority_queue
- 按照某种比较规则存放元素,每次出根元素
- 底层结构
- vector:默认
- deque
- 底层结构需要实现规定的接口
- pop_back
- 交换第一个和最后一个元素
- 调用pop_back
- 向下调整:保证堆的性质(最大堆或最小堆)
- push_back
- 调用pusha_back
- 向上调整:保证堆的性质
- front
- empty
- size
- pop_back
- 不提供迭代器
- 仿函数
- 也称为仿函数对象:在类中重载"()"运算符
- 和某些容器(比如priority_queue)或者算法(比如排序)配合使用,完成内部的元素的排列或者比较逻辑
- 空间配置器
- 算法
- algorithm
- sort
- find
- 其它
- algorithm
- 容器
- IO
- 标准IO
- cin
- 通过>>把控制台保存到内置类型当中
- istream对于内置类型,重载了“>>”
- 对于自定义类型,可以重载运算符“>>”,通过cin>>自定义类型对象,把控制台内容存放到自定义对象中
- 通过>>把控制台保存到内置类型当中
- cout、cerr、clog
- 通过<<把内存中的内置类型内容输出到控制台
- ostream对于内置类型,重载了"<<"
- 对于自定义类型,可以重载运算符“<<”,通过cout,cerr,clog<<自定义类型对象,把内存中自定义对象的内容输出到控制台
- 通过<<把内存中的内置类型内容输出到控制台
- cin
- 文件IO
- ifstream
- 二进制读入
- 按照内存的存放形式读入,按字节读取,字节流读取
- 文本读入
- 对二进制内容经过编码之后的字符进行读入,按字符读取,字符流读取
- 重载>>运算符
- 二进制读入
- ofstream
- 二进制写出
- 按照内存的存放形式写出,按字节写,字节流写出
- 文本写出
- 对二进制内容经过编码之后的字符进行写出,按字符写出,字符流写出
- 重载<<运算符
- 二进制写出
- 文本写出
- 重载<<运算符
- 二进制写出
- ifstream
- 标准IO