牛逼了，C++哈希表(std::unordered_map<type1, type2>)
1. 哈希表是什么哈希表（Hash table），也称散列表，是根据关键码的值而直接进行访问的数据结构。C++名字unordered_map或unordered_set，Set与Map一个是只有Value，另一个带Key。(Map,键值对)哈希表的增删改查的时间复杂度都是常数级，使用时特别高效。时间复杂度为o(1)，空间复杂度为o(n)。2. 哈希函数就是根据哈希key按照一定逻辑求出哈希value。3. 哈希碰撞多个value绑定到了同一个哈希key下面,当访问的时候，得到的vlaue不唯一。4. 哈希冲突的解决方法开放定址法线性探测法二次探测法随机探测法再散列函数法事先准备多个散列函数公共溢出区法建立两张表，一张为基本表，另一张为溢出表。链地址法（拉链法）冲突的数据全部存储在同一个线性链表中。5.常见的三种哈希结构1.数组2.set （集合） 集合 底层实现 是否有序 数值能否重复 数值能否更改 增删改查效率 std::set 红黑树 有序 否 否 O(logn) std::multiset 红黑树 有序 是 否 O(logn) std::unordered_set 哈希表 无序 否 否 O(1) 3.map（映射） 映射 底层实现 是否有序 key能否重复 key能否更改 增删改查效率 std::map 红黑树 有序 否 否 O(logn) std::multimap 红黑树 有序 是 否 O(logn) std::unordered_map 哈希表 无序 否 否 O(1) 6.添加数据std::unordered_map<std::string,std::vector<std::string>> unrdered_strings; std::string MyStr("123"); std::string TheStr("I LOVE YOU !"); unrdered_strings[MyStr].emplace_back(TheStr);
Makefile异常处理-"C++: error: exception handling disabled, use -fexceptions to enable"
Makefile异常处理：C++: error: exception handling disabled, use -fexceptions to enable原因分析这是因为你的工程中使用了C++的异常处理机制（try-throw），而编译器不推荐这种操作，所以默认不开启，需要手动在Makefile文件里添加。解决方法在Makefile文件中添加-fexceptions参数即可，添加在生成.o文件时例子# Makefile rules... .c.o : $(OBJECTS.C) $(CC) -I. $(CFLAGS) $(INCLUDES) $(MMAL_CFLAGS) -D__MMAL_ENCODER_DEBUG__ -c $< -o $@ .cc.o : $(OBJECTS.CC) $(CXX) -I. $(CFLAGS) $(CCFLAGS) $(MMAL_CFLAGS) $(INCLUDES) -c $< -o $@ -fexceptions -fpermissive # 增加 -fexceptions -fpermissive 参数，以使用MQTTCPP库的throw $(TARGET): $(OBJECTS) $(CXX) $(LDFLAGS) -o $(TARGET) -Wl,--start-group $(OBJECTS) $(BUILD_LIBS) -Wl,--end-group $(SYSLIBS) -lwiringPi -lbcm2835 -lpaho-mqttpp3 -lpaho-mqtt3a
C++类枚举的使用1 单独类枚举使用方法#include <iostream> enum class MyEnum ：unsigned char //冒号后是指定枚举数据类型为 unsigned char { one = 1,two,three }; int main() { std::cout << MyEnum::one << std::endl; }2 类内枚举使用方法#include <iostream> class MyClass { public: enum MyEnum {one = 0, two, three}; }; int main() { std::cout << MyClass::one << std::endl; }3 类枚举变量声明及使用#include <iostream> class MyClass { public: enum MyEnum {one = 0, two, three}; }; int main() { MyClass::MyEnum EENUM; EENUM = MyClass::one; //auto EENUM = MyClass::one;//可以使用自动类型 std::cout << EENUM << std::endl; }4 类枚举错误荟萃4.1 类枚举成员重复#include <iostream> class MyClass { public: enum MyEnum {one = 1, two, three}; enum MyEnum_1 {one = 1}; //错误，因为在类内one这个枚举元素是唯一的，已经在上一句中定义了 }; int main() { MyClass::MyEnum EENUM; EENUM = MyClass::one; std::cout << EENUM << std::endl; }4.2 类枚举变量赋值中，枚举名与枚举元素不对应#include <iostream> class MyClass { public: enum MyEnum {one = 1, two, three}; enum MyEnum_1 {four= 4}; }; int main() { MyClass::MyEnum EENUM; EENUM = MyClass::four; //错误，因为MyClass::four不是MyClass::MyEnum枚举名的成员，是MyClass::MyEnum_1 的 std::cout << EENUM << std::endl; }
基于VSCode和CMake进行C/C++开发的LInux平台环境配置Make与Makefile的关系make是个命令工具，解释Makefile中的指令，在make文件中描述了整个工程所有文件的编译顺序、编译规则Cmake是啥Cmake是一个跨平台的安装(编译)工具，可以用简单的语句来描述所有平台的安装(编译过程)。能够输出各种makefile或者project文件，能测试编译器所有支持的C++特性。Cmake的组态栏取名为CMakeLists.txt。Cmake并不直接构建出最终的软件，而是产生标准的构建档，然后再依照一般的构建方式使用。Cmake与CMakeLists的关系cmake是一个命令工具，可以用来生成makefile。但是也要根据CmakeLists.txt中的内容来生成,CMakeLists.txt就是写给cmake的规则。重点make是一个命令工具，Makefile是一个文件，make执行的时候，去读取Makefile文件中的规则，重点是makefile文件得自己写。cmake是个命令工具，CMakeLists.txt是一个文件,cmake执行的时候，去读取CMakeLists.txt文件中的规则，重点是CMakeLists.txt得自己写。一、开发环境搭建安装GCC,GDBsudo apt-get install build-essential gdb #安装成功确认 gcc --version g++ --version gdb --version安装CMakesudo apt-get install cmake #安装成功确认 cmake --version二 、GCC编译器-MakeFile文件目标文件:依赖文件 [tab] 指令 #先写输出后写输入，一步步递进 #伪目标 .PHONY: #rm -rf hello.o hello.S hello.i hello:hello.o gcc hello.o -o hello hello.o:hello.S gcc -c hello.S -o hello.o hello.S:hello.i gcc -S hello.i -o hello.S hello.i:hello.c gcc -E hello.c -o hello.i .PHONY:#伪目标 clear:#这是个操作名字 使用 make clear可以执行以下指令 rm -rf hello.o hello.S hello.i完事以后使用make指令即可伪目标(.PHONY:).PHONY:#伪目标 clear:#这是个操作名字 使用 make clear可以执行以下指令 rm -rf hello.o hello.S hello.i多文件#文件列表为 src.c src.h main.c main.h #可执行文件取名为output output:src.o main.o gcc src.o mian.o -o output src.o:src.c gcc -c src.c -o src.o main.o:main.c gcc -c mian.c -o main.o .PHONY: clearall:#这是个操作名字 使用make clearall 可以执行以下指令 rm -rf src.o main.o output变量(替换、追加、恒等于、使用)变量替换=、变量追加+=、常量恒等于:=、使用变量$(变量)#文件列表为 src.c src.h main.c main.h #可执行文件取名为test001 TAR = test #此时TAR为变量、可追加和改变 TAR += 001 #追加001 此时TAR = test001 CC := gcc #此时CC为常量、不可追加、不可改变 OBJ = src.o main.o #变量使用方法 $(TAR):$(OBJ) $(CC) $(OBJ) -o $(TAR) src.o:src.c $(CC) -c src.c -o src.o main.o:main.c $(CC) -c mian.c -o main.o .PHONY: clearall:#这是个操作名字 使用make clearall 可以执行以下指令 rm -rf src.o main.o output隐含规则(任意文件和所有文件)任意%.c、%.o所有*.c、*.o#文件列表为 src.c src.h main.c main.h #可执行文件取名为output TAR = output #此时TAR为变量、可追加和改变 #TAR += 001 #追加001 此时TAR = test001 CC := gcc #此时CC为常量、不可追加、不可改变 OBJ = src.o main.o #变量使用方法 $(TAR):$(OBJ) $(CC) $(OBJ) -o $(TAR) %.o:%.c#任意的.o文件由任意同名.c文件生成 $(CC) -c %.c -o %.o .PHONY: clear:#这是个操作名字 使用make clear 可以执行以下指令 rm -rf *.o #删除所有的中间文件通配符(所有目标文件、所有依赖文件、所有依赖文件的第一个文件)所有目标文件$^所有依赖文件$@所有依赖文件的第一个个文件名称$<所有时间戳比目标新的依赖文件，并以空格分开$?#文件列表为 src.c src.h main.c main.h #可执行文件取名为output TAR = output #此时TAR为变量、可追加和改变 #TAR += 001 #追加001 此时TAR = test001 CC := gcc #此时CC为常量、不可追加、不可改变 OBJ = src.o main.o RMRF = rm -rf #变量使用方法 $(TAR):$(OBJ) $(CC) $^ -o $@ %.o:%.c#任意的.o文件由任意同名.c文件生成 $(CC) -c $^ -o $@ .PHONY: clear:#这是个操作名字 使用make clear 可以执行以下指令 $(RMRF) $(OBJ) #*.o #删除所有的中间文件附、使用time计算代码运行时间time ./prog三、G++重要编译参数1. -g 编译带调试信息的可执行文件# -g 选项告诉GCC产生能被GNU调试器GDB使用的调试信息，以便调试程序 #产生带调试信息的可执行文件test g++ -g test.cpp -o test2. -O[n] 优化源代码#所谓优化，例如省略代码中从未使用过的变量、直接将常量表达式用结果值代替等等，这些操作会缩减目标文件所包含的代码量，提高最终生成的可执行文件的运行效率。 # -O 选项告诉g++对源代码进行基本优化。这些优化在大多数情况下都会使程序执行的更快，-O2选项告诉g++产生尽可能小的和尽可能快的代码。 # -O 等同于 -O1 同时减小代码的长度和执行时间。 # -O0 表示不优化 # -O2 除了完成-O1的优化外还进行一些额外的调整工作，如指令调整等。 # -O3 则包括循环展开和其他一些处理特性相关的优化工作 # 选项将编译的速度比使用-O时慢，但通常产生的代码执行执行速度会更快。 #使用 -O2 优化源代码，并输出可执行文件 g++ -O2 test.cpp3. -l 和 -L 指定库文件 | 指定库文件夹# -l参数(小写)就是用来指定程序要链接的库，-l参数紧接着就是库名 # 在 /lib 和 /usr/lib 和 /usr/local/lib 里的库直接用 -l 参数就能链接 #链接glog库 g++ -lglog test.cpp # 如果库文件没放在上面的文件夹里，需要使用 -L 参数(大写)指定库文件所在目录 # -L 参数跟着的是库文件所在的目录名 # 链接mytest库, libmytest.so在 /home/bing/mytestlibfolder 目录下 # -L后面没有空格 g++ -L/home/bing/mytestlibfolder -lmytest test.cpp #libmytest.so4. -I 指定头文件搜索目录# /usr/include 目录一般不用指定，gcc知道去哪里找，但是如果头文件不在/usr/include/里我们就要使用 -I参数指定了,比如头文件字啊/myinclude目录里，那编译命令行就要加上-I /myinclude 参数了，如果不加你会得到一个"xxx.h:No such file or directory"的错误。 # -I 参数可以使用相对路径，比如头文件在当前目录，可以使用 -I . 来指定。上面我们提到的-cflag参数就是用来生成 -I 参数的 # -I后面没有空格 g++ -I/myinclude test.cpp5. -Wall 打印警告信息# 打印出g++提供的警告信息 g++ -Wall test.cpp6. -w 关闭警告信息# 关闭所有警告信息 g++ -w test.cpp7. -std=c++11 设置编译标准g++ -std=c++11 test.cpp -o test8. -o 指定输出文件名# 指定即将产生的文件名 # 指定输出可执行文件名为test g++ test.cpp -o test9. -D 定义宏# 在使用gcc/g++编译时定义宏,不需要加空格 #常用场景： # -DDEBUG 定义DeBUG宏，可能文件中有关DEBUG宏部分的相关信息，用个DDEBUG来选择开启或者关闭DEBUG # 示例 #ifdef DEBUG printf("DEBUG"); #endif10.GCC英文手册man gcc11.【实战】g++命令行编译11.1 直接编译# 文件结构 yoola@ubuntu:~/WorkSpace/SWAP$ tree . . ├── include │ └── Swap.h ├── main.cpp └── src └── Swap.cpp #因为src/Swap.cpp 中写头文件#include “Swap.h” 、而Swap.h与Swap.cpp不在同一级目录 yoola@ubuntu:~/WorkSpace/SWAP$ g++ main.cpp src/Swap.cpp src/Swap.cpp:1:10: fatal error: Swap.h: No such file or directory 1 | #include "Swap.h" | ^~~~~~~~ compilation terminated. #指定包含头文件的路径 yoola@ubuntu:~/WorkSpace/SWAP$ g++ main.cpp src/Swap.cpp -Iinclude yoola@ubuntu:~/WorkSpace/SWAP$ ./a.out test11.2 生成库文件并编译11.2.1 链接静态库生成可执行文件①：# 进入src目录下 cd src # 汇编,生成Swap.o文件 g++ Swap.cpp -c -I../include # 生成静态库libSwap.a ar rs libSwap.a Swap.o # 回到上级目录 cd .. # 链接，生成可执行文件:staticmain g++ main.cpp -Iinclude -lSwap -Lsrc -o staticMain11.2.2 链接动态库生成可执行文件②:# 进入src目录下 cd src # 生成动态库libSwap.so g++ Swap.cpp -I../include -fPIC -shared -o libSwap.so #-fPIC使.so文件的代码段变为真正意义上的共享 # 上面命令等价于以下两条命令 #gcc Swap.cpp -OI../include -c -fPIC #gcc -shared -o libSwap.so Swap.o # 回到上级目录 cd .. # 链接，生成可执行文件sharemain g++ main.cpp -o sharemain -Iinclude -lswap -Lsrc # 可以看到与静态库执行代码相同，如果有静态库有动态库，则优先链接动态库11.2.3 动态库运行可执行文件#直接运行会报错 yoola@ubuntu:~/WorkSpace/SWAP$ ./sharemain ./sharemain: error while loading shared libraries: libSwap.so: cannot open shared object file: No such file or directory #应当指出动态运行库的位置、或者将动态库和可执行文件直接放在同一个文件夹下 LD_LIBRARY_PATH=src ./sharemain #输出结果 yoola@ubuntu:~/WorkSpace/SWAP$ LD_LIBRARY_PATH=src ./sharemain test四、GDB调试器前言GDB(GNU Debugger)是一个用来调试C/C++程序的功能强大的调试器，是Linux系统开发C/C++最常用的调试器程序员可以使用GDB来跟踪程序中的错误，从而程序员的工作量。Linux开发C/C++一定要熟悉GDBVSCode是通过调用GDB调试器实现C/C++的调试工作的Windows系统中，常见的集成开发环境(IDE)，如VS、VC等，他们内部已经嵌套了相应的调试器GDB主要功能：设置断点（断点可以是条件表达式）使程序再指定的代码行上暂停执行，便于观察单步执行程序，便于调试查看程序中变量值的变化动态改变程序的执行环境分析崩溃程序产生的core文件4.1 常用调试命令参数调试开始：执行gdb [exefilename]，进入gdb调试程序，其中exefilename为要调试的可执行文件名## 以下命令后括号内为命令的简化使用，比如run(r)，直接输入命令 r 就代表命令run $(gdb)helep(h) # 查看命令帮助，具体命令查询字在gdb中输入help + 命令 $(gdb)run(r) # 重新开始运行文件（run-text:加载文本文件，run-bin: 加载二进制文件） $(gdb)start # 单步执行,运行程序，停在第一行执行语句 $(gdb)list(l) # 查看源代码(list-n,从第n行开始查看代码。list+ 函数名：查看具体函数) $(gdb)set # 设置变量的值 $(gdb)next(n) # 单步调试（逐过程，函数直接执行） $(gdb)step(s) # 单步调试(逐语句：跳入自定义函数内部执行) $(gdb)backtrace(bt) # 查看函数的调用的栈帧和层级关系 $(gdb)frame(f) # 查看函数的栈帧 $(gdb)info(i) # 查看函数内部局部变量的数值 $(gdb)finish # 结束当前函数，返回函数调用点 $(gdb)continue(c) # 继续运行 $(gdb)print(p) # 打印值及地址 $(gdb)quit(q) # 退出gdb $(gdb)break+num(b) # 在第num行设置断点 $(gdb)info breakpoints # 查看当前设置的所有断点 $(gdb)delete breakpoints num(d) # 删除第num个断点 $(gdb)display # 追踪查看具体变量值 $(gdb)undisplay # 取消追踪查看变量 $(gdb)watch # 被设置观察点的变量发生修改时，打印显示 $(gdb)i watch # 显示观察点 $(gdb)enable breakpoints # 启用断点 $(gdb)disable breakpoints # 禁用断点 $(gdb)x # 查看x/20xw 显示20个单元，16进制，4字节每单元 $(gdb)run argc[1] argv[2] # 调试时命令行传参 $(gdb)set follow-fork-mode child # Makefile项目管理:选择跟踪父子线程(fork())TIPS:编译程序时需要加上-g，之后才能使用gdb进行调试: gcc -g main.c -o main回车键：重复上一命令nano显示行号ctrl + 3 + shift + 3ctrl + l 在gdb模式下清屏4.2 【实战】命令行调试给出一段简单代码，准备调试#include <iostream> using namespace std; int main(int argc, char **argv) { int N = 100; int sum = 0; int i = 1; //calulate sum from 1 to 100 while(i <= N) { sum = sum + i; i = i + 1; } cout << "sum = " << sum << endl; cout << "The program is over." << endl; return 0; }nano显示行号ctrl + 3 + shift + 3ctrl + l 在gdb模式下清屏Starting program: /home/yoola/WorkSpace/GDB/a_g sum = 5050 The program is over. [Inferior 1 (process 68496) exited normally] (gdb) break 17 Breakpoint 1 at 0x555555555205: file sum.cpp, line 17. (gdb) info break Num Type Disp Enb Address What 1 breakpoint keep y 0x0000555555555205 in main(int, char**) at sum.cpp:17 (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x0000555555555205 in main(int, char**) at sum.cpp:17 (gdb) b 18 Breakpoint 2 at 0x55555555523d: file sum.cpp, line 18. (gdb) i b Num Type Disp Enb Address What 1 breakpoint keep y 0x0000555555555205 in main(int, char**) at sum.cpp:17 2 breakpoint keep y 0x000055555555523d in main(int, char**) at sum.cpp:18 (gdb) 运行至断点后查看变量gdb output (gdb) b 17 Breakpoint 1 at 0x1205: file sum.cpp, line 17. (gdb) r Starting program: /home/yoola/WorkSpace/GDB/a_g Breakpoint 1, main (argc=1, argv=0x7fffffffde78) at sum.cpp:17 17 cout << "sum = " << sum << endl; (gdb) p sum $1 = 5050 (gdb) p N $2 = 100 (gdb) c Continuing. sum = 5050 The program is over. [Inferior 1 (process 68591) exited normally] (gdb) q跟踪变量输出、回车键继续执行上次命令(gdb) b 14 Breakpoint 1 at 0x11ff: file sum.cpp, line 14. (gdb) i b Num Type Disp Enb Address What 1 breakpoint keep y 0x00000000000011ff in main(int, char**) at sum.cpp:14 (gdb) r Starting program: /home/yoola/WorkSpace/GDB/a_g Breakpoint 1, main (argc=1, argv=0x7fffffffde78) at sum.cpp:14 14 i = i + 1; (gdb) display i 1: i = 1 (gdb) c Continuing. Breakpoint 1, main (argc=1, argv=0x7fffffffde78) at sum.cpp:14 14 i = i + 1; 1: i = 2 (gdb) c Continuing. Breakpoint 1, main (argc=1, argv=0x7fffffffde78) at sum.cpp:14 14 i = i + 1; 1: i = 3 (gdb) Breakpoint 1, main (argc=1, argv=0x7fffffffde78) at sum.cpp:14 14 i = i + 1; 1: i = 4 (gdb) 五、IDE-VSCode5.1 界面介绍略5.2 扩展安装1.c/c++ 2.CMAKE 3.CMAKE Tools5.3【实战】运行使用ctrl + ~调出Terminal(终端)视频链接->基于VSCode和CMake实现C/C++开发 | Linux篇格式化代码ctrl + shift + iRename Symbol(批量修改变量名)F2六、CMake前言CMake是一个跨平台的安装编译工具，可以用简单的语句来描述所有平台的安装(编译过程)。Cmake可以说是已经成为大部分C++开源项目标配6.1 Cross-platform development(跨平台编译)如果你有跨平台编译（交叉编译）的需求，假如你使用Windows平台的VS，OSX平台的OSX，Linux平台的Makefile。无CMake时添加一个新源文件，需要在各个平台的工程文件里分别修改有CMake时添加一个新源文件，只需修改CMakeLists即可6.2 CMakeLists.txt 语法特性介绍基本语法格式：指令（参数1,参数2...）参数使用括号括起来参数之间使用空格或者分号分开指令是大小写无关的，参数和变量是大小写敏感的set(HELLO hello.cpp) add_executable(hello main.cpp hello.cpp) ADD_EXECUTABLE(hello main.cpp ${HELLO})变量仍使用${}方式取值，但是在 IF 控制语句是直接使用变量名6.3 重要指令和CMake常用变量6.3.1 重要指令 cmake_minimum_required - 指定CMake的最小版本要求语法: cmake_minimum_required(VERSION versionNumber [FATA_ERROR])# cmake最小版本要求为3.0.0 cmake_minimum_required(VERSION 3.0.0)project - 定义工程名称，并可指定工程支持的语言（括号里为可选项）语法：project(projectname [CXX] [C] [Java])# 指定工程名为HELLOWORLD project(HELLOWORLD)set - 显式的定义变量语法: set(VAR [VALUE] [CAHE TYPE DOCSTRING [FORCE]])# 定义SRC变量，其值为main.cpp hello.cpp set(SRC sayhello.cpp hello.cpp)include_directories - 向工程添加多个特定的头文件搜索路径 --->相当于g++编译器的-l参数语法: include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)# 将/usr/include/myincludefolfer 和 ./include 添加到头文件搜索路径 include_directories(/usr/include/myincludefolder ./include)link_directories - 向工程添加多个特定的库文件 --->相当于指定g++编译器的-L参数语法: link_directories(dir1 dir2 ...)# 将/usr/lib/mylibfolder 和 ./lib 添加到库文件搜索路径 link_directories(/usr/lib/mylibfolder ./lib)add_library - 生成库文件语法: add_library(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)# 通过变量 SRC 生成 libhello.so 共享库 # SHARED 动态库 # STATIC 静态库 # MODULE 模块库 # 这里的STATIC和SHARED可不设置，通过全局的BUILD_SHARED_LIBS的FALSE或TRUE来指定 # windows下，如果dll没有export任何信息，则不能使用SHARED，要标识为MODULE add_library(hello SHARED ${SRC})add_compile_options - 添加编译参数语法: add_compile_options ( ...)add_compile_options(-Wall -std=c++11 -O2)add_executable - 生成可执行文件语法: add_executable(exename source1 source2 ... sourceN)# 编译main.cpp生成可执行文件main add_executable(main main.cpp)target_link_libraries - 为target添加需要链接的共享库 --->相同于指定g++编译器-l参数语法: target_link_libraries(target library1<debug | optimized> library2 ...)# 将hello动态链接库文件连接到可执行文件main target_link_libraries(main hello)add_subdirectory - 向当前工程添加存放源文件的子目录，并可以指定中间二进制和目标二进制存放的位置语法: add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])# 添加src子目录,src中需要一个CMakeLists.txt add_subdirectory(src)aux_source_directory - 发现一个目录下所有的源代码文件并将列表储存在一个变量中，这个指令临时被用来自动构建源文件列表语法: aux_source_directory(dir VARIABLE)# 定义SRC变量，其值为当前目录下所有的源代码文件 aux_source_directory(. SRC) # 编译SRC变量所代表的源代码文件，生成main可执行文件 add_executable(main ${SRC})6.3.2 CMake常用变量CMAKE_C_FLAGS gcc编译选项CMAKE_CXX_FLAGS g++编译选项# 在CMAKE_CXX_FLAGS编译选项后追加-std=c++ set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" )CMAKE_BUILD_TYPE 编译类型(Debug, Release)# 设定编译类型为debug, 调试时需要选择debug set(CMAKE_BUILD_TYPE Debug) # 设定编译类型为release, 发布时需要选择release set(CMAKE_BUILD_TYPE Release) # Debug自动添加-g参数，Release会上o3优化以及关闭debug调试CMAKE_BINARY_DIRPROJECT_BINARY_DIR<projectname>_BINARY_DIR这三个变量指代的内容是一致的。如果是in source build, 指的就是工程顶层目录如果是out-of-source编译, 指的是工程编译发生的目录PROJECT_BINARY_DIR跟其他指令稍有区别, 不过现在，你可以理解他们是一致的CMAKE_SOURCE_DIRPROJECT_SOURCE_DIR<projectname>_SOURCE_DIR这三个变量指代的内容是一致的，不论采用何种编译方式，都是工程顶层目录。也就是在in source build时，他跟CMAKE_BINARY_DIR等变量一致PROJECT_SOURCE_DIR 跟其他指令稍有区别, 不过现在，你可以理解他们是一致的CMAKE_C_COMPILER: 指定C编译器CMAKE_CXX_COMPILER: 指定C++编译器EXECUTABLE_OUTPUT_PATH: 可执行文件输出的存放的路径LIBRARY_OUTPUT_PATH: 库文件输出的存放路径6.4 CMake编译工程CMake目录结构: 项目主目录存在一个CMakeLists.txt文件两种方式设置编译规则包含源文件的子文件夹包含CMakeLists.txt文件，主目录的CMakeLists.txt通过add_subdirectory添加子目录即可；包含源文件的子文件未包含CMakeLists.txt文件，子目录编译规则体现在主目录的CMakeLists.txt中;6.4.1 编译流程在linux平台下使用CMake构建C/C++工程的流程如下:手动编写CMakeLists.txt命令执行cmake PATH生成Makefile(PATH是顶层CMakeLists.txt所在的目录)执行命令make进行编译。# important tips . # 表示当前目录 ./ # 表示当前目录 .. # 表示上级目录 ../ # 表示上级目录6.4.2 两种构建方式内部构建(in-source build)： 不推荐使用内部构建会在同级目录下产生一大堆中间文件，这些中间文件不是我们所需要的，和工程源文件放在一起会显得杂乱无章。# 内部构建 # 在当前目录下，编译本目录的CMakeLists.txt，生成Makefile和其他文件 cmake . # 执行make命令，生成target make外部构建(out-of-source build): 推荐使用将编译输出文件与源文件放到不同的目录中# 外部构建 # 1. 在当前目录下，创建build文件夹 mkdir build # 2. 进入build文件夹 cd build # 3. 编辑上级目录的CMakeLists.txt, 生成Makefile和其他文件 cmake .. # 4. 执行make命令，生成target make6.5 【实战】CMake代码实践【基于VSCode和CMake实现C/C++开发 | Linux篇】七、【实战】：使用VSCode进行完整项目开发案例士兵许三多有一把枪，叫做AK47士兵可以开火士兵可以给枪装填子弹枪能够发射子弹枪能够装填子弹 -- 增加子弹数量开发：开发枪类开发士兵类SoliderGunstring _name;string _type;Solider(string name); addBulletToGun(int num); fire();Gun(string type); addBullet(int num); shoot();7.1 项目应设置合理目录头文件应该写在include下源文件应该写在src下
算法一：使用 _finddata_t#include <io.H> #include <string> #include <deque> //注意，本算法使用filter（fileType）只能遍历目标文件夹folderPath下的对应类型文件 void dfsFolder(std::string folderPath, std::deque<std::string> & filenames, std::string fileType = "") { _finddata_t FileInfo; if (*folderPath.rbegin() != '\\')//检查原始文件夹名称是否以'\\'结尾 { folderPath += "\\"; } if (fileType == "")//如果filter不启用 { fileType = "*.*"; } else if (fileType == "*.*" || fileType == "*")//无需操作、主要在回调本函数时使用 { } else { fileType = "*." + fileType; } std::string fileFindPath = folderPath + fileType; std::cout << "当前的查找目录为" << fileFindPath << std::endl; long Handle = _findfirst(fileFindPath.c_str(), &FileInfo); if (Handle == -1L) { std::cerr << "can not match the folder path" << std::endl; //exit(-1); return ; } do { //判断是否有子目录 if (FileInfo.attrib & _A_SUBDIR) { //这个语句很重要 if ((strcmp(FileInfo.name, ".") != 0) && (strcmp(FileInfo.name, "..") != 0)) { std::string newPath = folderPath + FileInfo.name; dfsFolder(newPath, filenames, fileType); } } else { std::string newPath = folderPath + FileInfo.name; filenames.push_back(newPath); std::cout << newPath.c_str() << std::endl; } } while (_findnext(Handle, &FileInfo) == 0); _findclose(Handle); } int main() { std::deque<string> ddd; dfsFolder("D:", ddd, "jpg");// system("pause"); return 0; }算法二：使用 _finddata_t#include <io.H> #include <string> #include <deque> //可遍历出子文件里的对应文件，可用filter进行文件后缀的过滤 int listAllFile(std::string path, std::deque<std::string> & dstFilePathList, std::string fileTypeFilter = "") { _finddata_t FileInfo; bool needFilter = false; std::string fileFindPath; if (*path.rbegin() != '\\') { path += "\\"; } else { } fileFindPath = path + "*.*"; if (fileTypeFilter == "" || fileTypeFilter == "*.*" || fileTypeFilter == "*") { needFilter = false; } else { needFilter = true; } std::cout << "当前的查找目录为" << fileFindPath << std::endl; long Handle = _findfirst(fileFindPath.c_str(), &FileInfo); if (Handle == -1L) { std::cerr << "can not match the folder path" << std::endl; //exit(-1); return -1;//空文件件 } do { //判断是否有子目录 if (FileInfo.attrib & _A_SUBDIR) { //这个语句很重要 if ((strcmp(FileInfo.name, ".") != 0) && (strcmp(FileInfo.name, "..") != 0)) { std::string newPath = path + FileInfo.name; listAllFile(newPath, dstFilePathList, fileTypeFilter); } } else { if (needFilter != true)//如果不需要进行文件类型过滤 { std::string newPath = path + FileInfo.name; dstFilePathList.push_back(newPath); std::cout << newPath.c_str() << std::endl; } else if (needFilter == true && std::string(FileInfo.name).find(fileTypeFilter) != -1)//如果需要文件类型过滤,并且本文件类型对应正确 { std::string newPath = path + FileInfo.name; dstFilePathList.push_back(newPath); std::cout << newPath.c_str() << std::endl; } else { std::cout << "文件" << FileInfo.name << "与NameFilter=" << fileTypeFilter << "不符,故不加入对应目标文件夹列表" << std::endl; } } } while (_findnext(Handle, &FileInfo) == 0); _findclose(Handle); return dstFilePathList.size();//返回对应文件列表的大小 } int main() { std::deque<string> ddd; dfsFolder("D:", ddd, "jpg");// system("pause"); return 0; }算法三:使用CFileFind(MFC环境下)//没有用filter int listAllFile(const CString & path, std::deque<std::string> & FilePathList) { CFileFind stFileFind; CString FileFindPath; if (path.Right(1) != '\\') { FileFindPath = path + "\\*"; } else { FileFindPath = path + "*"; } bool fResult = stFileFind.FindFile(FileFindPath); if (false == fResult) { PrintSysLog("空文件夹，请重新选择"); return -1;//空文件夹 } while (TRUE == fResult) { fResult = stFileFind.FindNextFile(); //过滤掉“.”和“..” if (TRUE == stFileFind.IsDots())continue; else if (TRUE == stFileFind.IsDirectory())//如果是文件夹，则递归获取文件 { if (false == listAllFile(stFileFind.GetFilePath(), FilePathList))//递归 return FALSE; } else { CString strFileName = stFileFind.GetFilePath(); //查找自己需要的文件类型，继续做其他操作。 if (stFileFind.IsArchived())//档案(正常文件) 、可尝试自己加filter { FilePathList.push_back(strFileName.GetBuffer());//将文件夹的地址放入其中 } else if (stFileFind.IsCompressed())//压缩 { //MessageBox("Compressed"); } else if (stFileFind.IsDirectory())//目录 { //MessageBox("Directory"); } else if (stFileFind.IsSystem())//系统 { //MessageBox("System"); } else if (stFileFind.IsHidden())//隐藏 { //MessageBox("Hidden"); } else if (stFileFind.IsTemporary())//临时 { //MessageBox("Temporary"); } else if (stFileFind.IsNormal())//常规 { //MessageBox(strFileName); } else if (stFileFind.IsReadOnly())//只读 { //MessageBox(strFileName); } else { //MessageBox(strFileName); } } } stFileFind.Close(); std::ostringstream tempStr; tempStr << "获取到的文件有" << "\r\n"; for (auto listPos = FilePathList.begin(); listPos != FilePathList.end(); listPos++) { tempStr << *listPos << "\r\n"; } PrintSysLog(tempStr.str()); return 0; }
解决方法首先将出现这个错误应该怎么处理的方法写出来，解一些急着知道结果的攻城狮们燃眉之急：1.在主函数包含头文件时将实现模板类的函数也包含进来，如模板类在function1.cpp中实现，则在主函数中添加 #include "function.cpp"2.将模板类的实现方法写在头文件里面，如模板类的头文件为：function1.h，那么就把实现要用到模板类的函数实现方法写在这 里3.在实现模板类的文件中调用一下模板类————————————————版权声明：本文为CSDN博主「性感博主在线瞎搞」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。原文链接：https://blog.csdn.net/qq_41884002/article/details/99816073
C++11回调函数与注意事项回调函数就是一个通过函数指针调用的函数。如果你把函数的指针（地址）作为参数传递给另一个函数，当这个指针被用来调用其所指向的函数时，我们就说这是回调函数。回调函数不是由该函数的实现方直接调用，而是在特定的事件或条件发生时由另外的一方调用的，用于对该事件或条件进行响应。回调函数可以是静态类函数也可以是全局函数。回调函数机制：定义一个函数（普通函数即可）；将此函数的地址注册给调用者；特定的事件或条件发生时，调用者使用函数指针调用回调函数注意事项全局函数可以直接作为回调函数C++将类函数直接作为回调函数，必须是静态函数原因：大概原因是普通的C++成员函数都隐含了一个传递函数作为参数，即this指针，C++通过传递this指针给成员函数从而实现函数可以访问C++的数据成员。由于this指针的原因，使得一个普通成员函数作为回调函数时就会因为隐含的this指针问题使得函数参数个数不匹配，从而导致回调函数编译失败。 原文链接注：C++静态成员函数与静态数据成员不同，静态成员函数的作用不是为了对象之间的沟通，而是为了能处理静态数据成员。静态成员函数没有this指针。既然它没有指向某一对象，也就无法对一个对象中的非静态成员进行默认访问（即在引用数据时不指定对象名）。静态成员函数和非静态成员函数的根本区别：非静态成员函数有this指针，而非静态成员函数没有this指针。由此决定了静态成员函数不能访问本类中的非静态成员。静态成员函数可以直接引用本类中的静态数据成员，静态成员函数主要用来访问静态数据成员，而不访问非静态成员。原文链接使用方法全局函数-C写法#include <stdio.h> typedef int(*callback)(int, int); int add(int a, int b, callback p){ return (*p)(a, b); } int add(int a, int b){ return a + b; } int main(int argc, char *args[]){ int res = add(4, 2, add); printf("%d\n", res); return 0; }全局函数-C++写法1-(推荐写法)#include <iostream> typedef int(*callback)(int, int); int add(int a, int b) { return a + b; } int Print(callback p, int a, int b) { std::cout << p(a, b) << std::endl; return 0; } int main() { Print(add, 1, 2); system("pause"); return 0; }全局函数-C++写法2#include <iostream> int add(int a, int b) { return a + b; } int Print(int(*callback)(int, int), int a, int b) { std::cout << callback(a, b) << std::endl; return 0; } int main() { Print(add, 1, 2); system("pause"); return 0; }静态类函数无需指明类对象只能访问到类的静态变量，（除非将调用对象作为参数传入静态函数，不推荐）链接举例#include <iostream> typedef int(*callback)(int); class classA { public: static int classA::m_i; static int classA_cb(int i)//静态函数没有this指针，只能访问到静态成员 { m_i = 0; std::cout << i << std::endl; return m_i; }; }; int classA::m_i = 0; //静态成员是该类所有对象唯一的,静态成员使用之前必须初始化 int test(callback p, int i) { return p(i); } int main() { std::cout << test(classA::classA_cb, 1) << std::endl;//因为函数是静态的，所以，使用方式类似全局函数 system("pause"); return 0; }非静态类函数-指定类并提供调用的对象非静态函数，必须指定函数所在类，然后传入类的对象。如果是类调用自身的非静态成员函数，则不需要传入对象。#include <iostream> class MyClass; typedef void (MyClass::*CallbackFunc)(int); // 声明回调成员函数指针 class MyClass { public: void Func(int n)// 回调成员函数 { std::cout << __FUNCTION__ << " n = " << n << std::endl; } void test(CallbackFunc func, int n)// 在类的内部调用 { (this->*func)(n); } }; void test(CallbackFunc func, MyClass *obj, int n)// 在类的外部调用 { (obj->*func)(n); } int main() { MyClass obj; obj.test(&MyClass::Func, 10);//使用类调用类内的函数的方式 test(&MyClass::Func, &obj, 20);//使用类外调用类内函数的方式 system("pause"); return 0; }非静态成员函数-模板方式实现#include <iostream> class MyClass { public: void Func(int n)// 回调成员函数 { std::cout << __FUNCTION__ << " n = " << n << std::endl; } }; class OtherClass { public: void Func(int n)// 回调成员函数 { std::cout << __FUNCTION__ << " n = " << n << std::endl; } }; template<typename T, typename A> void test(void (T::*method)(A), T *obj, const A& a) { (obj->*method)(a); } int main() { MyClass obj; test(&MyClass::Func, &obj, 10); OtherClass other; test(&OtherClass::Func, &other, 20); system("pause"); return 0; }声明函数指针数组template <typename PanelClass> typedef void(PanelClass::*FlashAllAreaCB[10])();声明函数指针容器-存疑//会报错，不知为啥 template <typename PanelClass> typedef void(PanelClass::*FlashAllAreaCB[10])(); std::list<FlashAllAreaCB> mylist;//提示FlashAllAreaCB未定义示例1-类模板+回调函数#include <iostream> #include class panelClass { public: void Func1()// 回调成员函数 { std::cout << __FUNCTION__ << " n = " << std::endl; } }; template<typename T> class classA { typedef void(T::*callback)(); T * mobj; callback mcb; public: classA(callback cb, T * obj); void flushs(); }; template<typename T> classA<T>::classA(callback cb, T * obj) { mcb = cb; mobj = obj; } template<typename T> void classA<T>::flushs() { (mobj->*mcb)(); } int main() { panelClass pc; classA<panelClass> ca(&panelClass::Func1, &pc); ca.flushs(); system("pause"); return 0; }示例2-类模板+回调函数一个类classA中声明另一个类panelClass向panelClass中填入需要由其调用的回函数Func1在需要的时候，panelClass会代替classA调用Func1函数（例如多线程定时调用）#include <iostream> #include template<typename T> class classA { typedef void(T::*callback)(); T * mobj; callback mcb; public: classA(callback cb, T * obj) :mcb(cb), mobj(obj){}; void flushs(){ (mobj->*mcb)(); }; }; class panelClass { public: panelClass(){ ca = new classA<panelClass>(&panelClass::Func1, this); }; ~panelClass(){ delete ca; ca = NULL; }; classA<panelClass> * ca; void Func1()// 回调成员函数 { std::cout << __FUNCTION__ << " n = " << std::endl; } }; int main() { panelClass pc; pc.ca->flushs(); system("pause"); return 0; }
STL—常用算法零、概述算法主要是由头文件<algorithm>,<functhionla>,<numeric>组成。<algorithm>是所有STL头文件最大的一个，范围涉及到比较，交换，查找，遍历操作，复制，修改等等<numeric>体积很小，只包括几个在序列上面进行简单数学运算的模板函数<functhionla>定义了一些模板类，用以声明函数对象一、常用遍历算法1.for_each-遍历容器#include <iostream> #include <vector> #include <algorithm> using namespace std; void myPrint(const int & i) { cout << i << endl; } class mprint { public: void operator ()(const int & i) { cout << i << endl; } }; int main() { vector <int> mv; for (int i = 1; i<=3 ; i++) { mv.push_back(i); } //for_each(mv.begin(), mv.end(), myPrint);//自定义函数输出 for_each(mv.begin(), mv.end(), mprint());//仿函数输出法 system("pause"); }2.transform-搬运到另一个容器中transform(iterator beg1, iterator end1, iterator beg2,_func);beg1 源容器开始迭代器end1 源容器结束迭代器beg2 目标容器开始迭代器end2 目标容器结束迭代器_func 函数或者函数对象目标vector容器必须先分配好空间#include <iostream> #include <vector> #include <algorithm> using namespace std; int myTrans(const int & i) { cout << i << endl; return i; } class myTran { public: int operator()(const int & i) { return i+1; } }; void myPrint(const int & i) { cout << i << endl; } int main() { vector <int> mv; for (int i = 1; i<=5 ; i++) { mv.push_back(i); } vector<int> mv2; mv2.resize(mv.size());//需要重新设置大小、否则会报错 for_each(mv.begin(), mv.end(), myPrint);//仿函数法 transform(mv.begin()+1, mv.end(), mv2.begin(), myTran());//仿函数法 for_each(mv.begin(), mv.end(), myPrint);//仿函数法 system("pause"); }二、常用查找算法find //查找元素 find_if //按条件查找元素 adjacent_find //查找相邻重复元素 binary_search //二分查找法 count //统计元素个数 count_if //按条件统计元素个数1.find-查找指定元素查找指定元素，找到返回指定元素的迭代器，找不到返回结束迭代器find(iterator beg, iterator end, value)beg 开始迭代器end 结束迭代器value 查找的元素内置数据类型#include <iostream> #include <vector> #include <algorithm> using namespace std; void test() { vector<int> mv; for(int i =0; i<=10;i++) { mv.push_back(i); } //查找容器中是否有5这个元素 vector<int>::iterator it = find(mv.begin(), mv.end(), 5); if (it == mv.end()) { cout<<"error"<<endl; return; } else { cout << *it << endl; } } int main() { test(); system("pause"); return 0; } 自定义数据类型自定义数据类型必须重载自定义数据类型的==运算符#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; class Person { public: Person(int id, string name):m_id(id),m_name(name){}; int m_id; string m_name; //重载==号让find知道如何对比person数据类型 bool operator==(const Person &p) { return p.m_id == this->m_id; } }; void test() { vector<Person> mv; mv.push_back(Person(1,string("wangtaijie"))); mv.push_back(Person(2,string("sadad"))); mv.push_back(Person(3,string("ssdadasf"))); mv.push_back(Person(4,string("sdadasfasdwq"))); //查找容器中是否有5这个元素 vector<Person>::iterator it = find(mv.begin(), mv.end(), *(mv.begin()+1)); if (it == mv.end()) { cout<<"error"<<endl; return; } else { cout << it->m_id<< it->m_name<< endl; } } int main() { test(); return 0; }2.find_if-按条件查找find_if(iterator beg, iterator end, _pred); //按值查找元素，找到返回指定位置迭代器，找不到返回结束迭代器位置beg 开始迭代器end 结束迭代器_pred 函数或者谓词(返回bool类型的仿函数)内置数据类型#include <iostream> #include <vector> #include <algorithm> using namespace std; class myFind { public: bool operator()(int val) { return val>5; } }; void test() { vector<int> mv; for(int i =0; i<=10;i++) { mv.push_back(i); } //查找容器中是否有5这个元素 vector<int>::iterator it = find_if(mv.begin(), mv.end(), myFind()); if (it == mv.end()) { cout<<"error"<<endl; return; } else { cout << *it << endl; } } int main() { test(); system("pause"); return 0; } 自定义数据类型#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; class Person { public: Person(int id, string name):m_id(id),m_name(name){}; int m_id; string m_name; }; class myCreat { public: bool operator()(const Person &p) { return p.m_id > 3; } }; void test() { vector<Person> mv; mv.push_back(Person(1,string("wangtaijie"))); mv.push_back(Person(2,string("sadad"))); mv.push_back(Person(3,string("ssdadasf"))); mv.push_back(Person(4,string("sdadasfasdwq"))); vector<Person>::iterator it = find_if(mv.begin(), mv.end(), myCreat()); if (it == mv.end()) { cout<<"error"<<endl; return; } else { cout << it->m_id<< it->m_name<< endl; } } int main() { test(); return 0; }3.adjacent_find-查找两个相邻重复元素adjacent_find(iterator beg, iterator end);beg 开始迭代器end 结束迭代器返回第一个元素出现的位置4.binary_search-查找指定元素是否存在、有序序列的二分查找法bool binary_search(iterator beg, iterator end, value);注意:在无序序列中不可用beg 开始迭代器end 结束迭代器value 查找的元素找到返回true,否则返回false5.count-统计元素出现次数count(iterator beg, iterator end, value);beg 开始迭代器end 结束迭代器value 查找的元素自定义元素需要重载==运算符自定义类型#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; class Person { public: Person(int id, string name):m_id(id),m_name(name){}; int m_id; string m_name; bool operator==(const Person &p) { return p.m_id == this->m_id; } }; void test() { vector<Person> mv; mv.push_back(Person(1,string("wangtaijie"))); mv.push_back(Person(2,string("sadad"))); mv.push_back(Person(3,string("ssdadasf"))); mv.push_back(Person(2,string("sdadasfasdwq"))); int it = count(mv.begin(), mv.end(), *(mv.begin()+1)); if (it == 0) { cout<<"未找到"<<endl; return; } else { cout <<"重复次数"<<it<< endl; } } int main() { test(); return 0; }重复次数26.count_if-按条件统计元素个数count_if(iterator beg,iterator end, _pred);beg 开始迭代器end 结束迭代器_pred 谓词自定义数据类型#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; class Person { public: Person(int id, string name):m_id(id),m_name(name){}; int m_id; string m_name; }; bool check(const Person &p) { return p.m_id>=2; } void test() { vector<Person> mv; mv.push_back(Person(1,string("wangtaijie"))); mv.push_back(Person(2,string("sadad"))); mv.push_back(Person(3,string("ssdadasf"))); mv.push_back(Person(2,string("sdadasfasdwq"))); int it= count_if(mv.begin(), mv.end(), check); if (it == 0) { cout<<"未找到"<<endl; return; } else { cout <<"结果"<<it<< endl; } } int main() { test(); return 0; }结果3三、常用排序算法sort //对容器内元素进行排序 radom_shuffle //洗牌，指定范围内的元素调整次序 merge //容器元素合并，并储存到另一个容器中 reverse //反转指定范围内的元素1.sort-排序算法sort(iterator beg, iterator end, _Pred);beg 开始迭代器end 结束迭代器_Pred 谓词默认采用升序，降序则使用greater<T>(),默认降序使用自定义类型则需要自定义谓词(返回值为bool的函数)2.radom_shuffle-随机打乱radom_shuffle(iterator beg, iterator end);可以指定随机数种子srand((unsigned int)time(NULL)),需要包含<ctime>、不然每次生成的顺序就相同3.merge-容器合并merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);//容器元素合并，并存到另一容器中注意：两个容器必须是有序的beg1 容器1开始迭代器end1 容器1结束迭代器beg2 容器2开始迭代器end2 容器2结束迭代器dest 目标容器开始迭代器,生成的容器也是有序的，而且重复的元素会前后排好目标容器必须先分配好空间,否则程序会崩溃#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; void myPrintf(int & i) { cout << i << " "; } void test() { vector<int> mv; vector<int> mv2; for (int i = 0; i< 20 ; i++) { mv.push_back(i); } for (int i = 20; i< 23; i++) { mv2.push_back(i); } vector<int> target; target.resize(mv.size()+mv2.size());//目标容器必须先分配好空间,否则程序会崩溃 merge(mv.begin(),mv.end(),mv2.begin(),mv2.end(),target.begin()); for_each(target.begin(),target.end(),myPrintf); cout << endl; } int main() { test(); return 0; }4.reverse-顺序对调reverse(iterator beg, iterator end);//将容器前后顺序对调，beg 开始迭代器end 结束迭代器#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; void myPrintf(int & i) { cout << i << " "; } void test() { vector<int> mv; mv.push_back(19); mv.push_back(12); mv.push_back(23); mv.push_back(423); mv.push_back(21); for_each(mv.begin(),mv.end(),myPrintf); cout << endl; reverse(mv.begin(),mv.end()); for_each(mv.begin(),mv.end(),myPrintf); cout << endl; } int main() { test(); return 0; }19 12 23 423 21 21 423 23 12 19四、常用拷贝和替换算法copy //容器内指定范围的元素拷贝到另一个容器中 replace //将容器内指定的范围的旧元素改为新元素 replace_if //容器内指定范围满足条件的元素替换为新元素 swap //互换两个容器的元素1.copy-容器拷贝copy(iterator beg, iterator end, iterator dest);beg 开始迭代器end 结束迭代器dest 目标容器开始迭代器目标容器必须先分配好空间,否则程序会崩溃#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; void myPrintf(int & i) { cout << i << " "; } void test() { vector<int> mv; for (int i = 0; i< 20 ; i++) { mv.push_back(i); } vector<int> tar; tar.resize(mv.size()); for_each(mv.begin(),mv.end(),myPrintf); cout << endl; copy(mv.begin(),mv.end(),tar.begin()); for_each(mv.begin(),mv.end(),myPrintf); cout << endl; } int main() { test(); return 0; }2.replace-指定范围替换replace(iterator beg, iterator end, oldvalue, newvlaue);//将区间内的所有指定元素替换成新的元素beg 开始迭代器end 结束迭代器oldvalue 旧元素newvlaue 新元素所有对应元素都会被替换无返回值#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; void myPrintf(int & i) { cout << i << " "; } void test() { vector<int> mv; for (int i = 0; i< 12 ; i++) { mv.push_back(i); } for_each(mv.begin(),mv.end(),myPrintf); cout << endl; replace(mv.begin(),mv.end(),10, 2000); for_each(mv.begin(),mv.end(),myPrintf); cout << endl; } int main() { test(); return 0; }2 3 3 2 2000 20003.replace_if-条件替换replace_if(iterator beg, iterator end, _Pred, newvlaue);//将区间内所有满足条件的元素替换成指定元素beg 开始迭代器end 结束迭代器_pred 谓词newvalue 新的元素#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; void myPrintf(int & i) { cout << i << " "; } class myGreater { public: bool operator ()(int i) { return i>2; } }; void test() { vector<int> mv; mv.push_back(2); mv.push_back(3); mv.push_back(3); for_each(mv.begin(),mv.end(),myPrintf); cout << endl; replace_if(mv.begin(),mv.end(),myGreater(),2000); for_each(mv.begin(),mv.end(),myPrintf); cout << endl; } int main() { test(); return 0; }2 3 3 2 2000 20004.swap-互换两个容器内容swap(container c1, contain c2);两个容器类型必须相同c1容器c2容器#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; void myPrintf(int & i) { cout << i << " "; } void test() { vector<int> mv; for (int i = 0; i< 20 ; i++) { mv.push_back(i); } vector<int> mv2; for (int i = 232; i< 245 ; i++) { mv2.push_back(i); } for_each(mv.begin(),mv.end(),myPrintf); cout << endl; for_each(mv2.begin(),mv2.end(),myPrintf); cout << endl; swap(mv,mv2); for_each(mv.begin(),mv.end(),myPrintf); cout << endl; for_each(mv2.begin(),mv2.end(),myPrintf); cout << endl; } int main() { test(); return 0; }0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 232 233 234 235 236 237 238 239 240 241 242 243 244 232 233 234 235 236 237 238 239 240 241 242 243 244 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19五、常用算术生成算法算术生成算法属于小型算法，使用时包含头文件#include <numeric>accumulate //计算容器元素累计和 fill //向容器中添加元素1.accumulate-容器元素累加和accumulate(iterator beg, iterator end, value);//计算容器元素累加总和beg 开始迭代器end 结束迭代器value 起始值返回值：结果#include <iostream> #include <vector> #include <numeric> #include <string> using namespace std; void test() { vector<int> mv; for (int i = 0; i< 20 ; i++) { mv.push_back(i); } int result = accumulate(mv.begin(),mv.end(),0); cout<<result<<endl; } int main() { test(); return 0; }2.fill-填充指定元素fill(iterator beg, iterator end, value);beg 开始迭代器end 结束迭代器vlaue 填充值必须先用resize预留好空间#include <iostream> #include <vector> #include <numeric> #include <algorithm> #include <string> using namespace std; void myPrintf(int & i) { cout << i << " "; } void test() { vector<int> mv; mv.resize(20);//预留空间、必须用resize fill(mv.begin(),mv.end(),5); for_each(mv.begin(),mv.end(),myPrintf); cout << endl; } int main() { test(); return 0; }六、常用集合算法set_intersection //求两个容器的交集 set_union //求两个容器的并集 set_difference //求两个容器的差集1.set_intersection-求两个容器的交集set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);beg1 容器1开始迭代器end1 容器1结束迭代器beg2 容器2开始迭代器end2 容器2结束迭代器dest 目标容器开始迭代器,生成的容器也是有序的，而且重复的元素会前后排好两个容器集合必须是有序序列目标容器必须先分配好空间,否则程序会崩溃返回值是交集在目标容器输出结束的位置#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; void myPrintf(int & i) { cout << i << " "; } void test() { vector<int> mv; vector<int> mv2; for (int i = 0; i< 20 ; i++) { mv.push_back(i); } for (int i = 10; i< 30; i++) { mv2.push_back(i); } vector<int> target; for_each(mv.begin(),mv.end(),myPrintf); cout << endl; for_each(mv2.begin(),mv2.end(),myPrintf); cout << endl; target.resize(min(mv.size(),mv2.size()));//目标容器必须先分配好空间,否则程序会崩溃 vector<int>::iterator itEnd = set_intersection(mv.begin(),mv.end(),mv2.begin(),mv2.end(),target.begin());//返回值是交集在目标容器输出结束的位置 for_each(target.begin(),itEnd,myPrintf);//实际交集 cout << endl; for_each(target.begin(),target.end(),myPrintf); cout << endl; } int main() { test(); return 0; }0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 10 11 12 13 14 15 16 17 18 19 10 11 12 13 14 15 16 17 18 19 0 0 0 0 0 0 0 0 0 02.set_union-求并集set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);beg1 容器1开始迭代器end1 容器1结束迭代器beg2 容器2开始迭代器end2 容器2结束迭代器dest 目标容器开始迭代器,生成的容器也是有序的，而且重复的元素会前后排好两个容器集合必须是有序序列目标容器必须先分配好空间,否则程序会崩溃返回值是并集在目标容器输出结束的位置#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; void myPrintf(int & i) { cout << i << " "; } void test() { vector<int> mv; vector<int> mv2; for (int i = 0; i< 20 ; i++) { mv.push_back(i); } for (int i = 10; i< 30; i++) { mv2.push_back(i); } vector<int> target; for_each(mv.begin(),mv.end(),myPrintf); cout << endl; for_each(mv2.begin(),mv2.end(),myPrintf); cout << endl; target.resize(mv.size()+mv2.size());//目标容器必须先分配好空间,否则程序会崩溃 vector<int>::iterator itEnd = set_union(mv.begin(),mv.end(),mv2.begin(),mv2.end(),target.begin());//返回值是交集在目标容器输出结束的位置 for_each(target.begin(),itEnd,myPrintf); cout << endl; for_each(target.begin(),target.end(),myPrintf); cout << endl; } int main() { test(); return 0; }0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 0 0 0 0 0 0 0 0 03.set_difference-求差集set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);beg1 容器1开始迭代器end1 容器1结束迭代器beg2 容器2开始迭代器end2 容器2结束迭代器dest 目标容器开始迭代器,生成的容器也是有序的，而且重复的元素会前后排好两个容器集合必须是有序序列目标容器必须先分配好空间,否则程序会崩溃返回值是差集在目标容器输出结束的位置#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; void myPrintf(int & i) { cout << i << " "; } void test() { vector<int> mv; vector<int> mv2; for (int i = 0; i< 20 ; i++) { mv.push_back(i); } for (int i = 10; i< 30; i++) { mv2.push_back(i); } vector<int> target; for_each(mv.begin(),mv.end(),myPrintf); cout << endl; for_each(mv2.begin(),mv2.end(),myPrintf); cout << endl; target.resize(mv.size()+mv2.size());//目标容器必须先分配好空间,否则程序会崩溃 vector<int>::iterator itEnd = set_difference(mv.begin(),mv.end(),mv2.begin(),mv2.end(),target.begin());//返回值是交集在目标容器输出结束的位置 for_each(target.begin(),itEnd,myPrintf); cout << endl; for_each(target.begin(),target.end(),myPrintf); cout << endl; } int main() { test(); return 0; }0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
内建函数一、内建函数对象的意义分类算术仿函数关系仿函数逻辑仿函数必须包含<functional>头文件二、算术仿函数功能描述实现四则运算其中negate是一元运算，其他是二元运算仿函数原型template<class T> T plus<T> //加法仿函数 template<class T> T minus<T> //减法仿函数 template<class T> T multiplies<T> //乘法仿函数 template<class T> T divides<T> //除法仿函数 template<class T> T modulus<T> //取模仿函数 template<class T> T negate<T> //取反仿函数#include <iostream> #include <functional> using namespace std; void test1() { negate<int> mni;//取反 cout << mni(50) << endl; } void test2() { plus<int> mpi;//返回值，两个参数的类型底层是写的相同的，所以只用写一个 cout << mpi(10, 50) << endl; } void test3() { divides<int> mpi;//返回值的类型是int， cout << mpi(10, 3) << endl;//输出为3 } int main() { test1(); test2(); test3(); system("pause"); }三、关系仿函数template<class T> bool equal_to<T> //等于 template<class T> bool not_equal_to<T> //不等于 template<class T> bool greater<T> //大于 template<class T> bool greater_equal<T> //大于等于 template<class T> bool less<T> //小于 template<class T> bool less_equal<T> //小于等于#include <iostream> #include <vector> #include <functional> #include <algorithm> using namespace std; void myPrint(const int & elem) { cout << elem << endl; } void test1() { vector<int> mv; mv.push_back(10); mv.push_back(123); mv.push_back(123); mv.push_back(42); mv.push_back(5); for_each(mv.begin(), mv.end(), myPrint); sort(mv.begin(), mv.end(), greater<int>()); for_each(mv.begin(), mv.end(), myPrint); } int main() { test1(); system("pause"); }四、逻辑仿函数template<class T> bool logical_and<T> //逻辑与 template<class T> bool logical_or<T> //逻辑或 template<class T> bool logical_not<T> //逻辑非#include <iostream> #include <algorithm> #include <functional> #include <vector> using namespace std; void myPrint(int elem) { cout << elem << endl; } logical_not<int> m1; void mlogic_not(int & elem) { elem = m1(elem); } void test1() { vector<int> mv; mv.push_back(1); mv.push_back(0); mv.push_back(0); mv.push_back(1); mv.push_back(5); for_each(mv.begin(), mv.end(), myPrint); for_each(mv.begin(), mv.end(), mlogic_not); for_each(mv.begin(), mv.end(), myPrint); } int main() { test1(); system("pause"); }\\结果 1 0 0 1 5 0 1 1 0 0
谓词一、谓词概念返回bool类型的仿函数称为谓词如果operator()接收一个参数，叫一元谓词如果operator()接受两个参数，叫二元谓词1.一元谓词#include <vector> #include <iostream> #include <algorithm> using namespace std; class GreateV { public: bool operator()(int val) { return val > 5; } }; void test1() { vector<int> mv; for (int i = 0; i < 10; i++) { mv.push_back(i); } //查找容器中，有没有大于5的数字 //GreateV()是匿名的函数对象， vector<int>::iterator it = find_if(mv.begin(), mv.end(), GreateV());//返回一个迭代器，条件成立的位置 if (it == mv.end()) { cout << "未找到" << endl; } else { cout << "找到了大于5的数字" << *it <<endl; } } int main() { test1(); system("pause"); }2.二元谓词#include <iostream> #include <vector> #include <algorithm> using namespace std; class printVec { public: void operator()(vector<int> &mv) { for (vector<int>::iterator m = mv.begin(); m!=mv.end();m++ ) { cout << *m << endl; } } }; class Print { public: bool operator()(const int &mv1, const int &mv2) { return mv1 > mv2; } }; void test1() { vector<int> mv; printVec pv;//声明一个仿函数的类 mv.push_back(10); mv.push_back(20); mv.push_back(40); mv.push_back(30); //sort(mv.begin(), mv.end());//默认从小到大排序 sort(mv.begin(), mv.end(), Print());//从大到小 pv(mv); } int main() { test1(); system("pause"); }