置顶
- 这本书好乱啊。。。各个知识点零散分布
- 以下笔记是本人在有一定基础的情况下,根据自己的不足或者想复习一下而做的。像一些基础的if else都省略了,像常量指针,指针常量这些问题本人已经基本掌握,所以也没记。
- 看完之后,回顾一下,仍觉得有很多地方不甚理解,比如虚函数,懂虚表什么的,但仍觉的还有许多要注意的。这个只能在具体的项目中去实践了。还有这只是C++的语法细节,还有很多库的使用什么的,诶。
第三章:处理数据
- C++新增整型:unsigned long long, long long 至少64位
- using std::cout
- 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"; //字符串常量
- C++11的auto
- 和C中的auto不同,作用是编译器根据初始值自动推断数据类型
- 处理简单类型不必要,用处一般是较复杂的类型,比如STL中的迭代器自动推断。
std::vector<int> test;
auto p=test.begin();
第四章:复合类型
- 数组,char[]字符串,注意若要处理输入,注意\n是否在缓冲区内等等问题,看输入是否符合预期
- string(C++98标准)
- 结构struct,枚举enum,共同体union
- 指针数组,new int[10] 用delete[]释放,new int用delete释放
- 自动存储,静态存储,动态存储。实际上就是局部变量存在栈区,static存在数据区,new的存在堆区
- 数组的替代——vector,vector效率比[]低,但更安全,若想效率高点,可以用array<int,5> a;表示5个int类型的数组
第五章:循环和关系表达式
- 基于范围的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里面的值的话,要加&
}
- 其他的for(;?{}, while(){}, do{}while(), 没什么说的
第六章:分支语句与逻辑运算符
- 库
从C继承的(ctype.h)与字符相关的库
函数名称 | 作用与返回值 |
---|
isalnum© | 若c是字母或数字,返回true |
其他的 | 需要时自己搜吧,知道有这个功能就ok了 |
toupper© | 返回c的大写,不改变c |
tolower© | 返回c的小写,不改变c |
第七、八章:函数
- 函数指针,回调函数。可以用typedef简化函数声明
- 内联函数inline,可以替代#define
- 引用传值,默认参数
- 函数重载,参数可以数量和类型不同。只有返回值类型不同不算重载
- 函数模板,可以进行模板函数的重载,只要参数不一样就行
template<class T,class T2>
T test(T a,T b){
}
- 函数模板具体化
为了解决某些特殊类型的问题,比如结构体不能加减,可以单独写一个针对此结构体的类型函数,如下所示
template<> void test<Student>(Student,Student);
- 函数模板实例化
- 实例化和具体化不一样,实例化分为隐士和显示实例化,显示实例化如下,两者的书写格式主要是少个<>
- 实例化我理解是指定生成这种类型的函数,如下,生成一个参数为int的函数。不然的话得根据参数自己去生成
- 具体化是在声明的时候用的,实例化实在代码执行中用的
template void test<int>(int,int);
- 通过使用auto和decltype关键字,可以在模板函数中推导不确定的类型,这个需要的时候再查
template<class T1,class T2>
auto gt(T1 x,T2 y) -> decltype(x+y){
...
return x+y;
}
第九章:内存模型和名称空间
- 存储持续性
- 自动
- 静态
- 动态
- 线程存储持续性(C++11),上三个和C一样,这个是和线程声明周期一样长的,用thread_local声明
- 作用域和链接性都和C差不多,已经做过笔记不再赘述
- mutable 关键字声明的变量,即使加const也可以修改,用在结构体中
struct Student{
int id;
mutable string name;
};
const Student s;
//s的id不能改,但是名字能改
- 函数链接性
默认是外部链接
加上static后就变为内部链接 - 名称空间
namespace lvkou{
int id;
bool sex;
}
- using使用
- using声明 lvkou::id=1;
- using编译 using namespace lvkou;
- using替代typedef using xx=vector;
- 匿名名称空间
namespace{
int aa;
}
//等价于,即将作用域限制与本文件
static int aa;
第十章:对象和类
- 默认构造
- 不写构造函数的话自动生成默认构造
- 使用:Student s=new Student; //不加小括号代表使用默认构造------加小括号:Student()
- 只有一个参数的构造函数,接受参数类型到类类型的隐士转换
- 用explicit声明的构造函数,不能用于隐式转换
- 析构函数
- 若有堆空间,需要自己写析构函数释放,否则使用默认的析构函数就行
- 若是虚基类,一定要把析构函数声明为virtual
- 构造函数
- 写在函数里的是赋值,不是初始化,写在函数列表后面的是初始化,初始化顺序和声明顺序一样,和写的顺序无关
- private,protect,public的用法区别得会
- const成员函数
声明为const的函数,只能读取类成员,不能改变类成员,若在类使用过程中定义了一个const的对象,则不能调用非const的成员函数
void show() const{
...
}
- this的使用
- 一般在返回的时候使用,return *this,例如操作符重载
- const成员变量
第十一、十二章:使用类
- 运算符重载
- ++的两个重载注意一下,参数加int的是 ++i, 不加int的是i++
- 仿函数:重载()运算符
- 除了()、[]、->、=这四种不能重载为非成员函数外,其他函数都能写为非成员函数。
- 根据类设计,如果不是必须,把操作符重载写为非成员函数更好一些,尤其是为类定义类型转换时
Student operator=(const Student&){
return *this;
}
- 友元
//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;
}
- 拷贝构造/赋值构造
- 若有成员变量在堆区,要自己写拷贝构造函数进行深拷贝,否则只是浅拷贝
- 赋值构造是重写=操作符,和拷贝构造类似,不过应该返回引用类型
Student::Student(Student& s){
...
}
Student& operator=(const Student& s){
...
return *this;
}
- 调用拷贝构造的三种情况
- 析构函数的调用情况
- 局部,区域结束调用
- static,程序结束调用
- new, 手动delete,否则等到程序结束,由操作系统释放
- 定位new
我去还有这种操作,,,第一次学
就是指定new 后对象放置的空间
char* buf=new char[100];
Student* s1,*s2;
s1=new(buf) Student;
s2=new(buf) Student;
//s2把s1覆盖了
- C++11允许类内初始化,相当于默认值
第十三章:类继承
- 继承is-a, 包含has-a
- 三种继承, 只有public能被对象调用
基类访问特性 | 继承方法 | 子类访问特性 |
---|
public | public | public |
protect | - | protect |
private | - | no acess |
public | protect | protect |
protect | - | protect |
private | - | no acess |
public | private | no acess |
protect | - | no acess |
private | - | no acess |
无语,Markdown不支持单元格合并,写html又觉得太麻烦了
- 构造
- 派生类先调用基类构造函数,再调用自己的构造函数
- 若基类构造函数需要参数,在构造函数的初始化列表处传过去
- 多态
- 静态多态:重写基类方法,注意重写和重载的区别
- 动态多态:基类使用虚方法,注意若有虚方法,基类的析构函数最好也声明为virtual
- 为什么声明为虚析构?
因为若不声明,则派生类对象结束后会调用基类的析构方法,而不会调用自己的,可能造成内存泄露
- 为什么叫动态?
若基类存在虚方法,则会对其对象添加一个隐藏成员,保存一个指向函数地址数组的指针,这种数组叫虚函数表,表项为函数地址。在创建对象时,也继承虚表,若是派生类对象,则修改对应函数的地址,这样会额外占些空间,且程序速度降低
- 虚方法在派生类中不一定非要重写,虚函数表项保留基类函数地址,但不重写的话也没必要定义为派生类
- 构造函数不能是虚函数
- 如果重定义时和基类的参数不符合,可能会有警告。会覆盖基类的这个函数,不是重载
- 静态联编和动态联编
- 动态联编,还是有虚继承的时候,用指针运行时才确定对象类型
- 静态联编,不用指针,直接使用对象,编译期间确定对象类型
Base *b= new Son;//动态
Base b; //确定为Base基类类型了
- 抽象基类
- 纯虚方法,也叫接口,只定义,在子类中必须重写
- 顶基类一般全部定义为接口
virtual void eat3()=0;
第十四章:C++中的代码重用
- 通常,包含,私有继承,公有继承用于实现has-a的关系
- 最好使用包含,除非要重新定义虚函数,或者要用到基类的protect成员,否则用包含,容易理解
- 多重继承,典型的菱形继承问题,可以用虚基类,xxx:virtual public name{};
- 类模板 template
- 模板的具体化:隐式实例化,显式实例化,显式具体化。上面说过,不再赘述
- 模板别名 using aaa=std::vector
- 模板友元,非模板 友元,约束模板友元,非约束模板友元。这个需要的时候再细看。
第十五章:友元,异常和其他
- 友元类的使用
- 嵌套类的使用
- 异常处理
- abord(),stdlib.h库,直接退出程序,需要程序员自己判断什么时候调用
- 返回错误码,在函数参数用用一个参数传指针或引用来存错误码,还是需要程序员自己判断
bool test(int a,int* ERR_CODE){
......
}
//捕获异常
try{
}catch(char* s){
cout<<s<<endl;
}
//抛出异常
if(a==0){
throw "a is 0"
}
- return 和 throw的不同处理方式
- return返回上一个调用此函数的函数
- throw一直往上找到catch的语句,没有就abort()
- 基类引用可以执行派生类对象,这个在多重继承中有用,throw()参数若是对象的话,不能先捕获基类引用
- exception类,所有异常类的基类
try{
......
}catch(exception& e){
cout<<e.what()<<endl; //输入错误信息
}
- new 的异常处理
//new 失败时返回null
int* a=new(std::nothrow) int;
- 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类型,能获得对象信息
- 更严格的类型转换
- dynamic_cast
- const_cast 把const改为非const
- static_cast
- reinterpret_cast
第十六章:string类和标准模板库
- string的几种构造函数
- 字符串
- 迭代器
- n个字符
- 初始化列表(C++11)
- 截取字符串
- find的几种用法
- size(),capacity(),reserve()的区别
- reserve()是请求最小内存大小
- capacity()是返回总内存大小
- size()是使用的元素个数
- 智能指针
- 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两种都适用
- 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(),但二者类型不同
- 迭代器类型
//这个可以用于输出vector中的元素,等价于把
copy(v.begin(),v.end(),ostream_iterator<int>(cout," ))
//把v中的元素拷贝到输出迭代器,等价于对每个元素执行 cout<<v[i]<<" ";
- 容器类型
- 序列容器
- vector
- deque
- list 双向链表,容器插入或删除后,迭代器指向元素不变
- forward_list(C++11) 单链表,功能没有list强,但简单紧凑
- queue
- priority_queue 默认底层类是vector,总把最大元素放到队首,也可以自定义首部元素规则
- stack
- array(C++11)
- 关联容器
- 无序关联容器
关联容器底层实现是红黑树,无序关联容器底层实现是哈希表
- unodered_set
- unordered_multiset
- unorder_map
- unordered_multimap
- 函数对象(仿函数)
- transform(v.begin(),v.end(),v2.begin(),out,plus<>()) //更多transform学STL时再细写
- 看了一些例子,觉得transform用熟练了,无敌啊
- 算法组
- 非修改式序列操作 如find,for_each等
- 修改式序列操作 如transform,random_shuffle,copy等
- 排序和相关操作 如sort等
- 通用数字运算 前三个在,这个在
- 其他库
- valarray库
- 能方便的对数组内的数值进行计算,指出sum(),max(),min()等的计算。
- 没有.begin() .end()函数,想用可以 sort(begin(obg),end(obj));
- 用vectoc存数据,复制到valarray里面进行计算
- 模板initializer_list(C++11)
第十七章:输入输出和文件
- 输出格式控制:cout.width(5)等,但感觉不如printf方便好记
- iomanip,专门控制输出格式的头文件,常用的有setw(),setprecision(),setfill()
- 文件io
ofstream fout;
fout.open("xxx.txt");
fout<<"hello word!";
fout.close();
string rel;
ifstream fin;
fin.open("xxx.txt");
fin>>rel;
fin.close();
- 流状态检查
if(!fin.is_open()){
......//打开失败
}
- 文件模式
常量 | 含义 |
---|
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
- 讲真C++文件操作看着真费劲,不如fread,fwrite或者read,write
第十八章:C++新标准
- 复习前面介绍过的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也不会变
- 移动构造和右值引用
- 主要是利用右值引用写构造函数和赋值函数
- 对程序员来说重要的不是写右值引用的代码,而是使用利用右值引用实现移动语义库的代码
- 新的类功能
- 特殊的成员函数,在原有的四个默认情况下新增了两个
- 移动构造函数
- 移动赋值函数
原来的四个是: - 默认构造
- 默认拷贝构造
- 默认赋值
- 默认析构
- 默认的方法和禁用的方法
- Student() =default; //声明默认构造
- Student(const Student&) =delete //禁止拷贝构造,也可以设置成私有,但不如这个易理解
- 委托构造,构造中用构造
- 继承构造函数,就是如果自己没写,就调用基类的相应构造方法,不过派生类的成员变量要通过初始化列表自己初始化
Student(int id,string name,bool boy):b(boy),Base(id,name){
......
}
- 管理虚方法:override,final
要解决的问题:如果派生类重写了基类的虚方法,但是参数不同,这样不会重载,是直接覆盖,基类的那个就不能用了,为了解决这个问题,引入override。
而final是禁止派生类重写这个虚方法
//在派生类中
virtual void test() override{
......
}
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
- 元编程加强
- 语言变化