c++语言知识点汇总

时间:2024-06-26 18:02:50

c++ primer version-5 的整理

section 1

内置类型和自定义类型;

main函数的返回值:指示状态。0:成功;1:系统定义。

unix和win系统中,执行完程序可以使用echo命令得到返回值。

编译器不同?

iostream有四个IO对象,cin,cout 和 cerr clog (可以往流中写入数据)

输出运算符 << 的左侧必须是ostream对象,右侧为要打印的值,如果不是内置的类型,则需要运算符的重载(友元、成员函数,写法?);endl 除了结束该行,还可以刷新流,将buffer内容刷到设备;

命名空间,用于避免名字定义的冲突

当cin>>object作为循环判定条件时,效果是检测流的状态。有效:未遇到错误;无效:遇到无效输入 or 遇到文件结束符(eof:win使用ctrl z,unix 用 ctrl D)

edit-confile-debug 逐个修正bug

头文件的引用:如果是标准库,用<>,如果不是,使用"xx.h "

文件重定向:命令行 exefile_name <infile>outfile

----------------------------------------

section 2:变量与基本类型

int long 4B 以及long long 8B 的长度;当运算同时有带符号和不带符号的数,会转化为不带符号

可寻址最小单元 字节;存储基本单元 字(看计算机是64bit的)

0开头8进制;0×开头16进制

浮点数,指数部分用e表示,默认字面值是double

转义:常用的有 换行 \n

建议初始化每个内置类型变量

分离式编译——声明和定义;使用 extern 表示不定义,不初始化变量

命名规范:自定义类名大写开头,变量名小写

引用(这里指左值引用)必须初始化,需要且只能绑定在对象上,引用即别名

  • 指针和引用都实现了间接访问。区别:指针本身就是对象,无需定义赋初值;允许对指针赋值和拷贝,可以指向不同的对象

空指针 NULL(相当于用0初始化指针) or nullptr(新标准,特殊类型字面值,推荐使用)

void* 指针:存放任意类型对象的地址,只能拿来作比较,or 赋值给另外的void×指针。不能操作该类指针的对象,因为只表示内存空间

const 对象必须初始化,如果想在多个文件间共享const对象,需要在定义前加extern

如果引用指向的是常量,则应该申明为常量引用 ;如果不是,那么对于它的常量引用不能通过引用来修改它的值,只能通过它本身修改,就是说const int & num 和 int const & num(不能修改引用指向的值)不同

常量指针:顶层const(常量指针):int * const p,表示指针本身是常量,不能修改指针指向另外的对象;底层const(指向常量的指针),修饰的是*p,不能修改指向的值*p,可以换对象

aoto会忽略掉顶层const的特性

decltype 类型指示符,decltype(a) r,将r的类型设为a的类型,

constexpr(c++11) 常量表达式 会检查是不是常量表达式,在编译时会计算

typedef or using(新标准) 别名

头文件不能被定义两次——头文件保护符——#ifndef #define #endif

----------------------------------------------

section 3:字符串、向量、数组

头文件不用using namespace的声明

string s(n,'c') 结果为n个c的串;初始化使用等号的是拷贝初始化

字符串能直接比较,getline(is,s)从is中读取一行给s

empty函数和size函数;size函数返回size_type类型变量,是无符号整型数,注意unsigned 数和整型数比较和运算

处理字符:cctype中包含了很多比较函数,例如isalpha(c)如果是字母时为真,tolower(c)如果c是大写字母,输出对应小写字母,否则原输出

vector就是个类模板,创建时需要实例化,初始化方式;使用范围for时,vector的遍历序列大小不能发生改变;

迭代器的目的是通用,所有stl容器都可以使用;可以用auto变量指向 容器的begin or end返回值

字符数组 注意要留1个单位来保存空字符\0

数组也可以使用begin和end函数得到指针,end(a)表示a中最后元素的下一位置的指针

指针运算得到的类型是 ptrdiff_t 定义在cstddef头文件与机器相关的类型,是带符号的

strlen strcmp strcat strcpy 都是c风格的函数,c风格使用字符数组表示字符串,这在c++中也可以操作,且很多可以兼容运算,但不推荐

-----------------------------------

section 4:表达式

逻辑与和或的短路求值

少用++的后置版本,但是在*iter++形式广泛使用

sizeof 返回size_t类型

隐式类型转换 和 显式类型转换:static_cast(除了底层const外的从大类型到小类型的转换,也可以找回void×)  const_cast(底层const去掉const性质) reinterpret_cast(重新解释,很容易出错) dynamic_cast

---------------------------------------

section 5:语句

switch() case : default:

范围for语句,直接使用auto,如果还需要对变量更改,必须是auto &i

如果抛出了异常,但是没有catch,那么最终会转到terminate的标准库函数去处理,一般是程序非正常退出

标准异常:定义在4个头文件中:exception stdexcept

---------------------------------

section 6:函数

传参:传值(指针也是拷贝),传引用(避免拷贝,与实参绑定,c++推荐使用);引用形参还可以返回额外信息,如果不想改变,则可以加const

顶层const传值时会忽略掉,所以尽量使用常量引用

数组不能拷贝,所以只能传指针或者引用。由于指针不知道数组的大小,所以,通过标记或者使用前尾指针or传递一个数组大小来解决

数组的引用 表示为 int (&arr) [10] 必须加括号,同样指针也是 需要加括号,不然就是10个指针构成的数组

main函数可以传参,argc,argv【】;其中argv[0] 表示程序名

如果实参的个数不知道,且属于同一类型,可以使用 initializer_list<T> il 传参,就相当于vector一样,是一个灵活的模板类,支持迭代器,不过内部的元素是const类型

省略符形参 仅仅可用于通用的类型

不要返回局部对象的引用或指针

返回值可以是vector,直接return {元素1,元素2....}

返回数组指针同样需要加括号:int (*func(int i) ) [10] ,或者使用c++11 尾置返回类型: aoto func(int i) -> int (*) [10]

重载对于顶层const会忽略

auto 和 decltype对于指针和引用的表示还是需要加 × 和 &的

重载与作用域:c++名字查找发生在类型检查前,当前作用域内有匹配的名字时,会忽略外层的同名实体

默认实参:申明时直接用=号,省略的只能省略尾部

内联inline 减少调用开销,优化规模小的,流程直接的,频繁调用的函数,尤其在循环体内;不用于内联递归函数

除了内联,constexpr可以用于常量表达式,两者通常放在头文件内定义

调试帮助:assert预处理宏 NDEBUG预处理变量

函数指针:int (*pt) (int a,int b) ,则pt就是函数的指针,括号不能少;将函数名作为一个值,它将转化为函数的指针

--------------------------

section 7:类

this是一个常量指针,不能改变指向

构造函数初始化列表用:表示,初始化与赋值不同,如果构造的时候,有const或者引用对象,他们必须使用初始化列表,而不能通过赋值(先初始化后赋值)构造;初始化顺序与定义的先后顺序有关

构造函数可以委托,委托使用的函数放在初始化列表中

如果有动态分配的内存,则慎用默认合成的拷贝赋值和析构函数,使用string vector这种问题会少很多

类 使用class 则默认所有成员是私有的,使用struct,默认是公有的

explicit 禁止单实参的构造函数实施隐式转换,放在声明处,可以显式转换使用

聚合类:所有成员是public,没有定义构造函数,没有初始值,没有基类虚函数

字面值常量类,constexpr构造函数:函数体一般为空,或者只有return语句

static成员函数不能包含和使用this指针,在外部定义静态函数时,不用写static,只出现在声明中

非静态数据成员不能作为默认的实参,而静态数据成员可以

------------------------------

section 8:IO库

IO对象无拷贝或赋值

条件状态:s.eof(); s.fail(); s.good(); s.clear(flags);常用的

缓冲区刷新

打开文件 fstrm.open(s);创建打开: fstream fstrm(s);

文件模式:in out app(每次写操作前定位到文件末尾) trunc截断文件

strm.str() 函数返回strm保存的string的拷贝

------------------------

section 9:顺序容器

list双向链表,forward_list单向链表,array(固定大小数组);后两个是c++11新加的

容器支持的函数:swap以及<=这些运算符,反向容器reverse_iterator rbegin

迭代器 解引用 end其实是最后一个元素后面的位置;容器初始化,可以使用两个迭代器来拷贝

array定义:array<int,10> arrayname=array1;该式子合法,只要array1也是<int ,10>

顺序容器(除了array)外定义了assign,有两种形式,一种是用迭代器,另外一种使用大小-元素

swap函数很快,因为并没有元素本身的交换;只有array的swap才会真正交换;推荐使用非成员函数版本的swap

容器的插入:push_back, emplace_back,push_front(forward_list也支持), insert 多种形式,可用迭代器,或者大小指定范围和元素;emplace的操作是构造元素,而不是像push和insert一样拷贝元素

删除:pop_back pop_front  erase

forward_list有它特殊的插入 insert_after 和删除 erase_after 以及返回head前不存在的元素的迭代器 before_begin() , cbefore_begin(),操作的数都是迭代器后的数

list与forward_list支持特定算法:lst.remove(val), lst.reverse(), 还有merge sort unique 算法

resize(n,t)函数:可能会使之前的迭代器失效;当删除元素时,尾后迭代器肯定失效

容器会有capacity,表示不分配新内存在的容量

额外的string操作:构造函数:s(s1,pos) s(s1,pos,len) 从s1的pos位置开始拷贝len个字符到s   或者使用substr(pos,n)函数,n也可以省略;除了insert和erase外还支持 append(args) 和replace(range,args)

string搜索:find,rfind,find_first_of,find_last_of,find_first_not_of 函数,如果没找到会返回npos静态成员,是个很大的数

string与其他数值的转化:to_string(val), stoi(s,p,d) p是位置,d是s表示的进制  stod(s,p)

容器适配器:stack和queue(依靠deque实现,默认),priority_queue(默认vector实现):都可以基于某个容器来直接构造

stack:pop(),push(item),emplace(args),top();

queue:pop, push, emplace, top, back, front

--------------------------------

section 10:泛型算法

迭代器令算法不依赖于容器,算法不改变容器大小,都对一个range进行操作

fill(pos1,pos2,num) 将range内的元素变为num

back_inserter(容器) 插入迭代器 还有front_inserter

sort(pos1,pos2)  unique(pos1,pos2)返回不重复数的end迭代器位置;stable_sort()

lambda表达式(适用于在很少地方出现的简单操作,适合有捕获变量场景),可调用的对象,[capture list] (参数) -> 返回值类型 { } ;可以用auto f=[ ]...来表示一个lambda,捕获的是局部变量,可以值=或者引用&捕获。lambda可以方便写在函数的实参里面,就像函数指针;

如果是通过值捕获的,但是加了mutable,那么局部变量的值也是会最终改变的,和不是const &的引用类似

如果不使用lambda,使用函数,但是要捕获局部变量。可使用STL中的auto f2=bind(f,_1,_2,...)

迭代器:插入,反向,流,移动迭代器

find_if(beg,end,pred)查找第一个令pred为真的元素

--------------------------------

section 11:关联容器

map set的multi-和unordered形式

pair p.first p.second

set中的关键字是只读的,迭代器是const,所以用cbegin

插入:insert与emplace函数 m.insert({key,val})  返回的是一个pair(first,second),first指代的是迭代器,second指代是否插入成功,如果重复没插入则为false

删除:c.erase(元素),元素可以是单/双迭代器,或者是key

map可以进行下标访问,c.[key]

查找:find(key)返回迭代器,如果没有则返回end()位置   ; count(key)返回出现的次数;lower_bound(k),upper_bound(k), equal_range(k)返回的是等于k元素的范围,是pair类型

示例程序:文件单词转换

#include<iostream>
#include<fstream>
#include<sstream>
#include <map>
#include<algorithm>
using namespace std; void transform(ifstream &map_file, ifstream &input, ofstream &out){
map<string,string> trans;
string key,val,text;
while(map_file>>key&&getline(map_file,val)){
if(val.size()>1) trans.insert({key,val.substr(1)}); //skip the ' ' before the val
else trans.insert({key," "});
}
while(getline(input,text)){
stringstream ss(text);
string word;
bool flag=true;
while(ss>>word){
if(flag) flag=false;
else out << " ";
map<string,string>::iterator map_it=trans.find(word);
if(map_it!=trans.end()) out<<map_it->second;
else out<<word;
}
out<<endl;
}
} int main(){
ifstream input("input.txt");
ofstream out("out.txt");
ifstream map_file("map.txt");
transform(map_file,input, out);
return 0;
}

  无序容器的输出可以使用范围for语句 const auto &it: unordered_map;该容器在存储组织上类似于桶,依靠哈希函数

-----------------------------------------

section 12:动态内存

静态内存:static变量和全局变量;栈内存:局部变量; 两者都由编译器自动创建和销毁

堆:每个程序有一块*空间,动态分配的对象

智能指针(定义在头文件memory中):负责自动释放所指向的对象:shared_ptr, unique_ptr; weak_ptr弱引用,指向sharedptr

shared_ptr独有操作:auto p=make_shared<T>(), p.unique(), p.use_count()

原理:shared_ptr的析构函数会递减引用计数,如果为0则会销毁指向对象,释放内存

引用计数增加情况:初始化另外的sharedptr or 作为参数传递给函数 or 作为函数返回值

减小情况:被赋予了新值 or 被销毁

为什么使用动态资源:(1)程序不知道要使用多少对象;(2)不知道所需对象的准确类型;(3)需要在多个对象间共享数据(常见需求)

内存耗尽会抛出bad_alloc异常,动态内存不能不释放。也不能多次释放

p.reset(new xxx)操作,使p指向另外的对象

使用智能指针管理资源,可以传递给它一个删除器,用于指定析构,可以是函数指针,也可以是可调用的lambda

unique_ptr是不支持拷贝的和赋值的,但是也可以reset或者置空;

weak_ptr是弱共享,不会改变计数值,使用lock()函数来返回一个指向相同对象的shareedptr

allocator类,在memory下,分配未构造的内存,将内存分配和对象构造分离,这样就可以按需构造

-------------------------------

section 13:拷贝控制

拷贝构造函数的第一个参数必须是引用类型

拷贝赋值运算符: Foo & operator=(const Foo&)  最后返回一个*this

析构的顺序与构造的顺序相反,先函数体,再成员,成员也是逆序的

什么时候调用析构:变量离开作用域;对象被销毁;容器被销毁;delete 某动态生成对象的指针;临时对象,创建它的完整表达式结束时

析构函数体自身不直接销毁成员,成员是在析构函数体之后隐含的析构阶段被销毁

法则:(1)需要自定义析构,则也需要拷贝和赋值操作;(2)需要拷贝,则也需要赋值,反之亦然,而析构不一定需要

可以使用默认的合成函数,=default;可以阻止拷贝或者赋值,=delete

赋值函数一定要注意将自己赋值给自己 ,能够正常操作

引用计数是作为一个指针变量出现在类的成员中的,指向真正的引用计数值

右值引用&&:变量是左值,只有const的左值引用 or 右值引用可以引用右值

移动move(头文件utility):比拷贝高效,相当于窃取资源:int &&rr2=std::move(rr1); rr1将会不使用,被销毁

为什么要移动:拷贝资源有额外的开销,在这种拷贝不是必需的情况下,可以定义移动构造函数和移动复制运算符的类来避免

noexcept:告知标准库不抛出异常

移动迭代器:解引用返回的是右值引用

引用限定符& or &&

-----------------

section 14:重载和类型转换

非成员函数的运算符重载:通常在形参上包含不止一个对象;成员函数的重载:一般IO运算符不使用成员函数重载,使用非成员或者友元

ostream &operator<<(ostream &os, const Sals &item)

一些算数或者关系运算符的重载,通常使用非成员,形参使用常量的引用

下标运算符[] 返回的是普通引用或者是常量引用

前后置的++使用成员函数来重载

除了函数指针,lambda外,调用还可以使用函数对象类,重载()

类型转换运算符

赋值,下标,函数调用和箭头运算符必须作为类的成员

-----------------------------

section 15:面向对象程序设计

多态-动态绑定-看传入的对象是到底是什么类型的,是基类还是派生类对象

虚函数-override-当通过指针或引用调用时,会动态绑定,运行时确定

派生类最好通过调用基类的构造函数来初始化基类中的成员

final  防止继承

派生类的成员或者友元只能通过派生类对象来访问基类受保护的成员,不能直接通过基类对象来访问

友元可以访问该类的私有部分

名字查找先于类型的检查

基类和派生类的虚函数的参数类型必须相同

在容器中可以存放基类的指针,可以是智能指针

-----------------------------------

section 16:模板与泛型编程

类型名可以用class 或者 typename表示 都可以

在实例化模板的时候,才生成代码,才会发现很多类型上的错误

模板的头文件通常包括声明和定义

类模板 template <typename T> class

using可以来设置模板的别名: using twin pair<T,T>     twin<string> str;

静态变量在模板类中,不同模板实例会有各自独有的static对象,只在相同类型的模板类*享

默认模板的实参 可以在typename T 后加 =

普通类中也可以有成员模板

多个文件实例化相同的模板会有开销,可以使用extern关键字标识模板声明,extern template class Blob<string> 则它会去外边找已经定义了的该模板定义

引用折叠:右值引用的右值引用会折叠成右值引用

move其实是函数模板,利用引用折叠

可变参数模板:参数包(模板参数包和函数参数包) template <typename T, typename ... Args> ; void foo(const T &t, const Args& ... rest) ; rest是函数包的名字

STL中算法都是函数模板,容器都是类模板

类型转换:有标准库定义的类模板,可将给定的模板类型参数转换为一个相关的类型

---------------------------------------

section 17:标准库特殊设施

tuple类似于pair(头文件tuple),可以包含多种不同类型的元素;使用aotu item0=get<0> (item)来访问其中的成员,比较操作要求两边成员个数都要相同

可以使用tuple来返回多个值,tuple可以包含在vector中

<bitset>:处理超过最长整形类型大小的位集合 bitset<n> b(u) ;其中 u是unsigned long long 类型,n是位的大小

也可以从string初始化,但是string 下标最大的数存在bitset的低位(下标为0)

bitset函数:count:置位的数目;test(pos)是否被置位

正则表达式(头文件regex

regex r(pattern) ; smatch results保存结果; regex_search(test_str, results, r) 找到1个就结束

随机数 random头文件:随机数引擎类和分布类

defaut_random_engine e, e()

uniform_int_distribution<unsighed> u(0,9)     u(e)

u和e可以声明为静态,以保证起始点不同。种子的概念,可以以time为种子

cin.get(char ch) 读入一个字符

流随机访问:seek和tell 用于文件流

-------------------------------

section 18:用于大型程序的工具

异常 抛出 catch 栈展开  最终到terminate中处理 终止当前程序

析构函数不要抛出自己不能处理的异常

抛出指针要求在对应的代码存在的地方,指针所指的对象必须在

重新抛出 就是 空throw ,捕获所有异常就是catch(...)

异常类:派生有runtime_error, logic_error, 异常类体系,使用我们自己的异常类

命名空间:防止命名污染,通过namespace::来访问特定的名字(如果有重复)

using声明: 每次只引入空间内的一个成员,在局部作用域有效;using指示:所有名字可见

using指示多了,则引入的名字也多,名字污染可能严重,二义性可能增加;所有有时用声明比较好

多重继承中,拷贝构造函数和赋值函数的构造顺序都一样,析构相反

虚继承:解决基类的多重拷贝,共享的基类子对象为虚基类;构造顺序:先由最底层的派生类调用虚基类的构造函数,再进行间接基类的构造,再直接基类的构造,最后自己的构造;析构相反顺序

--------------------------

section 19:特殊工具与技术

RTTI运行时类型识别

基类到派生类的转换 dynamic_cast:指针类型的转换,如果不成功返回0;引用类型转换,不成功抛出bad_cast异常

typeid(e) 返回e的类型,顶层const会忽略; 比较两个typeid可以在运行时识别

enum枚举:限定作用域与不限定的枚举,枚举成员是const的;enum intvalue : unsigned long long 在:后可以加类型

类成员指针:数据成员指针/成员函数指针;

嵌套类:可以使用访问限定符来控制外界对其成员的访问权限,与外层类是相互独立的

union:节约空间的类;可以有多个数据成员,但是在任何时候只有一个数据成员可以有值;默认公有

匿名union不能包含受保护的成员或私有变量,也不能定义成员函数

局部类:局部类的成员收到严格限制,只能访问外层作用域定义的类型名、静态变量和枚举成员,不能那个访问局部变量;外层函数不能访问局部类中私有成员

不可移植的特性支持:

位域:通常用无符号类型保存一个位域 unsigned int mode:2;表示2位; 指针无法指向位域

volatile限定符:当对象的值可能在程序的控制或检测之外被改变时,告诉编译器不应对这样的对象优化

链接指示:使用其它语言的代码:extern "C " {}