头文件后缀名: .h
源文件后缀名: .c
头文件后缀名: .h, .hpp, .hxx
源文件后缀名:.cpp, .cc, .cxx, .C .c++
.hpp: 声明和实现都有
.inl: 内联函数
使用inline修饰的具体函数实现,非声明。编译期间对于编译器承认的inline函数,将会直接把代码拷贝到函数调用位置。
#include会将头文件复制,同一个东西头文件实现一次,源文件实现一次就会触发重定义。
int argc : 传入参数个数,默认为1const char * argv[] : 输入的参数列表
argv[0] : 被执行程序(.exe文件)所在的绝对路径。gcc与g++进行文件编译时,会互相调用, .c文件除外; 链接时,不会互相调用。
流程: 预处理,编译,汇编,链接。
定义:表示创建变量或分配存储单元
声明:说明变量的性质,但并不分配存储单元;在 链接 时,查找具体定义。
变量
// 声明 extern int i; // 声明又定义 int i; int i = 1; extern int i = 1; // 函数外部才能被初始化
源文件中的全局变量 int a; 形式,默认修饰符为extern。
{ }的就是定义,否则就是声明。编译器关键字,会调用构造器与析构器。malloc/free为库函数实现。
// 限定内容 const int*p; int const *p; // 限定指针 int*const p; // 都限定 const int*const p;
不可变的 := 可变的 ,反过来不行。
配合 virtual 关键字使用;修饰子类 override 函数。
会指示编译器这部分代码按C语言的进行编译,而不是C++的;能够正确实现C++代码调用其他C语言代码。
使用include将另一个文件全部包含进去可以引用另一个文件中的变量,但是这样做的结果就是,被包含的文件中的所有的变量和方法都可以被这个文件使用,这样就变得不安全。如果只是希望一个文件使用另一个文件中的某个变量还是使用extern关键字更好。
可以让外部函数或者外部类,访问私有类的私有属性与函数。
\n 才会将写入内容加载到缓冲区\n 也算一个字符,会被加载到缓冲区cin >>
连续从键盘读取想要的数据,以空格、tab 、换行为分隔符。
char a; int b; float c; string cin>>a>>b>>c;
当 cin >> 从缓冲区中读取数据时,若缓冲区中第一个字符是空格、tab或换行这些分隔符时,cin>> 会将其忽略并清除,继续读取下一个字符; 若缓冲区为空,则继续等待。但是如果读取成功,字符后面的 空白符号 是残留在缓冲区的,cin>> 不做处理。
不想略过空白字符,那就使用 noskipws 流控制。
cin >>的返回值为 cin ;当输入 EOF (windows:ctrl+z, Linux:ctrl+d)时, cin >> 会返回0。
int a; // 当输入 EOF 时,可以终止循环 while(cin >> a){ }
cin.get()
缓冲区没有东西时,会堵塞等待。
int get(); istream& get(char& var); istream& get( char* s, streamsize n ); istream& get( char* s, streamsize n, char delim);
读取字符 :
```c++
#include <iostream>
using namespace std;
int main() {
char a;
char b;
a=cin.get();
cin.get(b);
cout << a << b <<endl;
return 0;
}
```
- <font color="#f44336">从输入缓冲区读取单个字符时不忽略分隔符,直接将其读取。</font>
读取行
c++ istream& get(char* s, streamsize n) istream& get(char* s, size_t n, char delim)
- s ,接收字符串用的数组
- n ,读取个数。实际读取个数为 n - 1 ,留了一个给 \0
- delim ,指定终止符
- 换行符(结束符)会被留在缓冲区, 但末尾其余空白符会被读取。
cin.getline() 读取行
istream& getline(char* s, streamsize count); //默认以换行符结束 istream& getline(char* s, streamsize count, char delim);
getline(cin,string,"结束符") 功能更强。传入的是 cin ,不是 stdin istream &ignore(streamsize num=1, int delim=EOF);
num 个字符,或遇到终止符 delim 结束(包括终止符)。ignore() ** 会阻塞等待,最好别用 EOF 做终止符**。num : 可以设置一个很大的数#include <limits>
numeric_limits<std::streamsize>::max();
fflush(stdin);
::int x; // Global x int main() { int x = 10; // Local x cout << "Value of global x is " << ::x; cout << "\nValue of local x is " << x; return 0; }
在类之外定义函数
访问类的全局变量
class Test { public: static int x; }; int Test::x = 1; void main(){ Test::x; }
如果有多个继承,父类变量名重名,子类可以做区分。
两个命名空间重命名
访问内部类
#include<iostream> using namespace std; class outside { public: int x; class inside { public: int x; static int y; int foo(); }; }; int outside::inside::y = 5; int main(){ outside A; outside::inside B; }
#include<> :表示只从从标准库文件目录下搜索,对于标准库文件搜索效率快。
#include"" :表示首先从用户工作目录下开始搜索,对于自定义文件搜索比较快,然后搜索标准库。
| 储存 | 初始化 | 二次赋值 | 形式 | |
|---|---|---|---|---|
| 指针 | 有储存空间 | NULL | 可以 | 类名 * |
| 引用 | 无空间,别名 | 已有的对象 | 不行 | 类名 & |
c语言没有引用机制,是靠c++扩展实现。
| 描述 | 储存 | |
|---|---|---|
| 类 | 模板,包括属性和行为 | 无 |
| 对象 | 类的实例化,主要是数据的集合 | 主要只储存了属性,储存位置看具体情况 ; 函数放代码段,所有对象通用 |
通过库函数
// 初始化赋值 std::unique_ptr<int> p1(new int(5)); // 移交所有权,p1将变无效 std::unique_ptr<int> p2 = std::move(p1); // 滞后赋值 unique_ptr<string> p3; p3 = unique_ptr<string>(new string ("You"));
// make_shared生成 std::shared_ptr<int> p1 = std::make_shared<int>(10); // 拷贝赋值 std::shared_ptr<int> p2; p2 = p1;
野指针不是NULL指针,是未初始化或者未清零的指针,在程序中乱指。
| 二次修改 | 参与运算 | sizeof | |
|---|---|---|---|
| 指针 | 可以 | 可以 | 指针的字节 |
| 数组 | 不行 | 可以 | 声明空间的字节 |
\0" " : 自带 \0strcpy,strncpy 不会在末尾加 \0,dest还得手动加终止strcat : 会自动加 \0 #include <string.h>
// 创建
char str[] = "RUNOOB";
char *str = "fuck you";
char str[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};
// 拷贝字符串
strcpy(char *dest, const char *src); // 将src拷贝到dest,拷贝以 \0 作终止;
char *strncpy(char *dest, const char *src, size_t n); // n指定长度,更安全
// 拼接字符串
char *strcat(char *dest, const char *src); // 往dest上加字符串
// 字符串长度
strlen(str); // \0 终止
// 比较字符串
int strcmp(const char *s1, const char *s2); // 相同返回 0
// 查找
char *strchr(const char *str, int c); //返回第一个字符位置,没有返回NULL
char *strstr(const char *haystack, const char *needle); // 返回第一个字符串位置,没有返回NULL
string 是一个对象string 的结尾没有结束标志 \0== 可以用来判断字符串是否相同#include <string> // 创建 string str; // s1的值为NULL string str = "c plus plus"; string str(c风格); // 字符串长度 str.length(); // 转c风格 str.c_str(); // 访问字符 str[i]; // 数字转字符串 string to_string(int value); string to_string(long value); string to_string(double value); // 字符转数字 atoi(const char*); stoi(const string&); strtoi(const char *); // 插入 string& insert (size_t pos, const string& str); // 删除 string& erase (size_t pos = 0, size_t len = npos); // 获取子串 string substr (size_t pos = 0, size_t len = npos) const; // 查找 返回第一个找到的位置,没找到返回一个无穷数 size_t find (const string& str, size_t pos = 0) const; size_t find (const char* s, size_t pos = 0) const; find_first_of(const string& str); // 子字符串和字符串共同具有的字符在字符串中首次出现的位置
函数名相同就会屏蔽父类同名函数
注 :
用户能定义自己的C语言库函数,连接器在连接时自动使用这些新的功能函数。这个过程叫做重定向C语言库函数。
虚函数是动绑定,运行时确定。
当私有继承和保护继承时,父类指针(引用)无法指向子类。默认为私有继承。 防止多重继承,出现属性多次定义,继承时,还要使用 virtual 进行修饰。
在子类中均存在但不可访问
公有继承基类的private
保护继承中基类的private
私有继承中基类的private和protected和public,
静态类型:对象在声明时的类型,在编译期既已确定;
动态类型:通常是指针或引用所指对象的类型,是在运行期决定的;
/** * A 类是声明,是obj的静态类型 * B 类是具体实例,是obj的动态类型 */ A* obj = new B();
静态绑定:绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期;
动态绑定:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期;可以进行二次修改。
A* obj = NULL; obj->fcn();
上面的函数也是能正常运行的,因为obj在编译时静绑定。
A a; // 初始化 A b = a; A c(a); // 赋值 b = a;
ASCII 码: 使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符。
BCD码: (Binary-Coded Decimal)亦称二进码十进数或二-十进制代码。用4位二进制数来表示1个十进制数中的0~9这10个数码。
内码: 指汉字系统中使用的二进制字符编码。
vptr 放第一个,接着是父类的非static属性,最后放子类的属性运行时地址起始位置:它芯片公司指定的一开始运行代码的位置。
运行地址: 就在从运行时地址起始位置(包括起始位置)往后排都是运行时地址。(程序代码被搬过来执行)
链接地址起始位置:链接脚本设定,这个位置在程序链接之后,就会确定下来。(用来计算偏移量的伪地址,之后重定向)
链接地址: 就是从链接地址起始位置(包括起始位置)往后排都是链接地址。(类似汇编地址)
加载地址: 从flash的那个地方开始读取程序,加载内存中去。
存储地址: 程序存储在哪儿的。
创建的是原进程的子进程,子进程会复制父进程的PCB(进程控制块),二者之间代码共享,数据独有,拥有各自的进程虚拟地址空间。
#include <unistd.h> pid_t fork(void);
正常终止
status 可以通过 wait(int *status) 接收; 会刷新缓冲#include <stdlib.h> void exit(int status);
status 可以通过 wait(int *status) 接收; 不会刷新缓冲#include <unistd.h> void _exit(int status);
异常退出
如果子进程先于父进程退出,而父进程并没有关心子进程的退出状况,从而无法回收子进程的资源,就会导致子进程变成僵尸进程。进程等待的作用就是父进程对子进程收尸!父进程通过进程等待的方式,回收子进程的资源,获取子进程的退出状态。
wait
#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status);
waitpid
//头文件同wait的头文件 pid_t waitpid(pid_t pid, int *status, int options);
低16位存放信息,高16位不用
高8位 : 退出码, exit 的传入值或者 return 值
(status >> 8) & 0xFF;
低8位 : 异常退出信息
(status >> 7) & 0x1;
status & 0x7F;
STL(Standard Template Library),即标准模板库,是一个具有工业强度的,高效的C++程序库。该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法。
主要构成有六大组件:
set map multi set multimap 通过红黑实现,无序 unoder_map unorder_set 通过哈希表实现。
容器类自动申请和释放内存,无需new和delete操作。
*;++;==;!=;= 。Container c; Container::iterator it; for(it=c.begin();it!=c.end();it++){ // vector 获取值 *it; // map 获取键值对 it->first; it->second; }
算法部分主要由头文件
钩子函数,可以当参数传递,让具体算法进行调用。 系统定义好的仿函数: <functional>
函数指针实现
仿函数实现:
operator() 操作符,本质上就定义了一个符号函数。#include <stdio.h> #include <algorithm> #include <vector> class Compare{ public: bool operator()(int a,int b){ return a > b; } }; int main(int argn,const char* args[]){ vector<int> vec(5,1); vec[0] = 2; vec[1] = 1; vec[2] = 9; vec[3] = 3; vec[4] = 1; // 排序算法 sort(vec.begin(),vec.end(),Compare()); // 实际的函数调用 bool flag = Compare()(10,12); }
注意:
Compare()是定义了一个临时对象,作为参数传递给了sort();若sort()没有采用引用传递,则对象将是值传递。
对基础容器进行功能扩展,标准库提供了三种顺序容器适配器:
| 容器 | 头文件 | 默认容器 | 可选容器 | 说明 |
|---|---|---|---|---|
| queue | <queue> | deque | list、deque | 基础容器必须提供push_front()运算 |
| priority_queue | <queue> | vector | vector、deque | 基础容器必须提供随机访问功能 |
| stack | <stack> | deque | vector、list、deque |
#include <stack> #include <vector> stack<int> s; stack< int, vector<int> > stk; //覆盖基础容器类型,使用vector实现stk s.empty(); //判断stack是否为空,为空返回true,否则返回false s.size(); //返回stack中元素的个数 s.pop(); //删除栈顶元素,但不返回其值 s.top(); //返回栈顶元素的值,但不删除此元素 s.push(item); //在栈顶压入新元素item
queue<int> q; //priority_queue<int> q; q.empty(); //判断队列是否为空 q.size(); //返回队列长度 // 出队和入队 q.push(item); //对于queue,在队尾压入一个新元素 q.pop(); //删除 queue 中的第一个元素 // 队列访问 q.front(); //返回队首元素的值,但不删除该元素 q.back(); //返回队尾元素的值,但不删除该元素
#include <vector> // 创建 vector<T> vec; vector<T> vec(int nSize); // 指定容器初始大小 vector(int nSize,const T& initValue):// 指定容器初始值 // 增加 vec.push_back(const T& item); // 在屁股添加 iterator vec.insert(iterator it,const T& item); // 迭代器指定位置插入元素 // 删除 vec.pop_back(); // 删除屁股 iterator erase(iterator it); // 删除迭代器指定位置元素 vec.clear(); // 删除全部 // 查找 vec[i]; vec.at(i); // 会检查是否越界,提高稳定性 // 容器内储存元素个数 vec.size(); // 容器是否有东西 vec.empty(); // 迭代器位置 vector<int>::iterator it = vec.begin(); // 起始位置 vector<int>::iterator it = vec.end(); // 结束位置 int a = *it; //获取值 *it += 1; // 定义二维数组 vector< vector<int> > obj(row); // row定义行数
排序:
> : 从大到小排序
< : 从小到大排序,默认方式
class Compare{ public: // 对象最好用引用进行传递 bool operator()(T const & a,T const & b){ return a > b; } }; // 调用函数 sort(vec.begin(),vec.end(),Compare());
key 不允许修改//头文件 #include<map> // 创建 map<int, string> map; // 使用{}赋值是从c++11开始的,因此编译器版本过低时会报错,如visual studio 2012 map<int, string> map = { { 2015, "Jim" }, { 2016, "Tom" }, { 2017, "Bob" } }; map<int, string,Compare> map; // 指定排序规则 // 迭代器 map<int,string>::iterator it = map.begin(); map<int,string>::iterator it = map.end(); it->first; // key it->second; // 值 // 添加key value pair<iterator,bool> state;// 能判断是否插入成功;插入key存在时,返回false state = map.insert(pair<int, string>(1, "student_one")); state = map.insert(map<int, string>::value_type (1, "student_one")); map[key] = value; // key存在就覆盖,key没有就创建 // 查找 // 关键字查询,找到则返回指向该关键字的迭代器,否则返回指向end的迭代器 // 根据map的类型,返回的迭代器为 iterator 或者 const_iterator iterator find (const key_type& k); const_iterator find (const key_type& k) const; // 删除 // 删除迭代器指向位置的键值对,并返回一个指向下一元素的迭代器 iterator erase( iterator pos ); // 根据Key来进行删除, 返回删除的元素数量,在map里结果非0即1 size_t erase( const key_type& key ); // 删除一定范围内的元素,并返回一个指向下一元素的迭代器 iterator erase( const_iterator first, const_iterator last ); // 清空map,清空后的size为0 void clear(); // 查询map是否为空 bool empty(); // 查询map中键值对的数量 size_t size(); // 查询关键字为key的元素的个数,在map里结果非0即1 ,可用于检测key是否包含 size_t count( const Key& key ) const;
排序:
> : 从大到小排序< : 从小到大排序,默认方式map<string,int,Compaer>map 对 key 进行排序。map 不允许对 key 进行修改,所以 operator()引用与函数必须为 const class Compare{ public: bool operator()(const string & a,const string & b) const { return a.c_str()[0] < b.c_str()[0]; } }; map<string,int,Compare> names;
O(log(n))。#include <set> // 创建 set<string> names; set<string,Compare> names; // 指定排序规则 // 添加 pair<set<int>::iterator, bool> ret = names.insert(name); // 删除 names.erease(name); names.clear(); // 查找,找不到返回 end() set<string>::iterator it = names.find(name); // 迭代器 set<string>::iterator it = names.begin(); set<string>::iterator it = names.end(); *it; // 计数 names.count(name); names.size(); names.empty();
排序:
> : 从大到小排序< : 从小到大排序,默认方式set 不允许对 key 进行修改,所以 operator()引用必须为 const class Compare{ public: bool operator()(const string & a,const string & b) const { return a.c_str()[0] < b.c_str()[0]; } }; set<string,Compare> names;
#include <deque> // 创建 deque<string> que; // 首尾操作 que.push_back(item); que.pop_back(); que.push_front(); que.pop_back(); que.front(); que.back(); // 删除 que.erase(it); que.clear(); // 查找 没有查找 用 #include<algorithm> 库中的 find
#include<math.h>
// 绝对值
int abs(int);
double fabs(double);
// 四舍五入
double round(double);
// 取整
double ceil(double num); // 向上取整
double floor(double num);// 向下取整
// 余数
double fmod(double num,double base);
%; 只能用于int;
\b : 字符边界. : 除 \n 以外所有字符\w : 等价于 [(0-9)(a-z)(A-Z)(_)] ,数字,字母,下划线\W : 上面取反 [^(0-9)(a-z)(A-Z)(_)]\d : 数字\D : 上面取反\s : 空白符(空格,制表符,换行)\S : 上面取反regex_search : 只返回第一次匹配到的子串\ #include <regex> // 定义表达式 regex reg("[a-z0-9]+"); // 是否匹配 bool regex_match(string,regex);//全匹配 bool regex_search(string,regex);//子串匹配,只匹配第一次找到的 // 捕获 bool reg_search(string,smatch,regex); smatch.size(); //匹配到的个数: 表达式匹配 + 捕获匹配 smatch.str(0); // 整个正则匹配到的部分 smatch.str(i); // 0之后的,都是捕获部分 smatch.prefix().str(); // 未匹配的前部分 smatch.prefix().str();// 未匹配的后部分
动态规划问题的一般形式就是求最值。
# 初始化 dp[] = init # 边界条件 dp[0][0][...] = base # 进行状态转移 for 状态1 in 状态1的所有取值: for 状态2 in 状态2的所有取值: for ... dp[状态1][状态2][...] = 求最值(选择1,选择2...)
dp[i][j] = min{ dp[i-1][j] + 1, dp[i][i-1] + 1, dp[i-1][j-1] + 1 }
当从dp[i-1][j-1]过度到dp[i][j]的求解有多种路径时,就存在解重叠的情况。采用备忘录和dp数组解决。

- 确定二维表
dp[][]初始值- 确定递推式
dp[i][j]的依赖情况(上图左边)- 根据依赖关系填充二维表,满足横向填充或者纵向填充
// i = 0, j = 0 的情况是边界条件,已经初始化好了。 for (int i = 1; i <= m; i++){ for (int j = 1; j <= n;j++){ dp[i][j] = f(dp[i-1][j-1],dp[i][j-1],dp[i-1][j]); } }