安装并配置编译器 MinGW MinGW 下载地址  注意打开页面后往下拉,选中x86_64-posix-seh 这个版本下载 
MinGW 百度云备用下载 x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z  
把下载好的 MinGW 压缩包解压到任意一个目录,然后配置环境变量到 bin 目录, 然后使用下面的命令测试,有输出就配置成功了。
MinGW 提供的头文件目录 mingw64\x86_64-w64-mingw32\include  其他编译器用到 MinGW 的头文件时,需要使用 -I  参数主动指定 MinGW 头文件目录
1 2 emcc .\src\main.cpp -o .\wasm\wasm_api.js -s WASM=1 -ID:\MinGW\mingw64\x86_64-w64-mingw32\include 
头文件简介 .cpp 文件可以引用.h 头文件,也可以不引用.h 头文件, 如果.h 头文件又引入了其他.h 头文件,那么.cpp 文件需要引入头文件 编译时要指定.h 头文件对应的.cpp 文件,
a.h
1 2 3 4 5 6 7 8 namespace  example {    class  MyMath  {     public :         static  int  PI;         static  int  add (int  a, int  b)  ;         static  int  sub (int  a, int  b)  ;     } } 
a.cpp
1 2 3 4 5 6 7 8 9 10 11 #include  "a.h"  namespace  example {    int  MyMath::PI = 3.1415926 ;     int  MyMath::add (int  a, int  b)   {         return  a + b;     }     int  MyMath::sub (int  a, int  b)   {         return  a - b;     } } 
main.cpp
1 2 3 4 5 6 7 #include  "a.h"  using  namespace  example;int  main ()   {  return  MyMath::add (MyMath::PI, 2 ); } 
编译
1 gcc main.cpp a.cpp -o main.exe 
非系统库的引入 当你的项目需要引入一个第三方库,但是它独立于系统库且不属于你的项目,那么就需要在编译时使用-I 参数来引入头文件。 而在项目的代码里,就可以使用尖括号引入头文件,例如:
1 2 g++ -I"E:\mylib\include"  your_source_file.cpp -o your_output_executable 
库操作 操作 dll 库 编译 dll 库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include  <Windows.h>  extern  "C"  {    __declspec(dllexport) int  GetScreenWidth ()   {         return  GetSystemMetrics (SM_CXSCREEN);     }     __declspec(dllexport) int  GetScreenHeight ()   {         return  GetSystemMetrics (SM_CYSCREEN);     }     __declspec(dllexport) COLORREF GetColor (int  x, int  y)   {         HDC hdcScreen = GetDC (NULL );         COLORREF pixelColor = GetPixel (hdcScreen, x, y);         ReleaseDC (NULL , hdcScreen);         return  pixelColor;     } } 
1 g++ -shared -o aaa.dll aaa.cpp 
调用没有头文件的 dll 目录结构为:
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 #include  <iostream>  #include  <windows.h>  int  main ()   {    HINSTANCE hDll = LoadLibrary (TEXT ("../lib/my.dll" ));       if  (hDll != NULL ) {                  typedef  int  (*MYPROC) (void )  ;         MYPROC myFunction = (MYPROC) GetProcAddress (hDll, "dllFunction" );         if  (myFunction != NULL ) {                          int  result = myFunction ();             std::cout << "Result: "  << result << std::endl;         } else  {             std::cerr << "Failed to get function pointer."  << std::endl;         }         FreeLibrary (hDll);       } else  {         std::cerr << "Failed to load DLL."  << std::endl;     }     return  0 ; } 
编译
1 2 3 g++ src/main.cpp -o output/main.exe -Llib -lmy 
字符编码 C++编程要注意字符编码,不同的字符编码要进行转换,其中有三个重要的字符编码环境:
操作系统字符编码 C++源码文件编码 输出字符串编码 操作系统字符编码 一般是指通过终端调用可执行文件 exe 时传入的中文字符串编码:
这里传入 main.exe 的中文字符串的编码就是使用的操作系统的字符编码,一般为 936.
查看系统字符编码 C++源码文件编码 1 2 3 4 5 6 #include  <iostream>  int  main ()   {    char  *str = "你好世界" ;     return  0 ; } 
这里字符串”你好世界”就是源码文件使用的编码,一般为 UTF-8。
输出字符串编码 通常是由其他程序调用可执行程序 exe 后拿到的输出字符串的编码。
1 2 3 4 5 6 7 #include  <iostream>  using  namespace  std;int  main ()   {    cout << "你好世界"  << endl;     return  0 ; } 
如果是在终端调用可执行文件 exe,那么输出的字符串就是使用的操作系统的字符编码,一般为 936。
如果是在其他程序调用可执行文件 exe,那么输出的字符串就是使用的其他程序的字符编码。
1 2 3 4 5 6 7 8 9 const  { exec } = require ("child_process" );exec ("main.exe" , (error, stdout, stderr ) =>  {  if  (error) {     console .error (stderr);   } else  {          console .log (stdout);   } }); 
比如这里的输出字符串 stdout 就是使用的 UTF-8 编码。
宽字符和窄字符 宽字符 wchar_t *和窄字符 char *,当他们需要相互转换的时候,必须要考虑编码问题。
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 30 31 32 33 34 35 #include  <windows.h>  using  namespace  std;char  *wchar2char (wchar_t  *wchar, int  icharset, int  ocharset)  {    int  bufferSize = WideCharToMultiByte (icharset, 0 , wchar, -1 , NULL , 0 , NULL , NULL );     char  *schar = new  char [bufferSize];     WideCharToMultiByte (ocharset, 0 , wchar, -1 , schar, bufferSize, NULL , NULL );     return  schar; } wchar_t  *char2wchar (char  *schar, int  icharset, int  ocharset)  {    int  bufferSize = MultiByteToWideChar (icharset, 0 , schar, -1 , NULL , 0 );     wchar_t  *wchar = new  wchar_t [bufferSize];     MultiByteToWideChar (ocharset, 0 , schar, -1 , wchar, bufferSize);     return  wchar; } 
Windows 窗口操作 根据窗口句柄获取窗口标题 1 2 3 4 5 6 7 8 9 10 11 #include  <windows.h>  wchar_t  *GetWindowTitle (HWND hwnd)  {    int  length = GetWindowTextLengthW (hwnd);     wchar_t  *buffer = new  wchar_t [length + 1 ];     GetWindowTextW (hwnd, buffer, length + 1 );     return  buffer; } 
遍历查找与指定窗口标题相似标题的窗口句柄 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include  <windows.h>  BOOL CALLBACK EnumWindowsProc (HWND hwnd, LPARAM lParam)   {    wchar_t  *buffer = GetWindowTitle (hwnd);     if  (wcsstr (buffer, (wchar_t  *)lParam) != NULL )     {         cout << "{"  << endl;         cout << "\"title\": "                  << "\""  << wchar2char (buffer, 65001 , 936 ) << "\","  << endl;         cout << "\"hwnd\": "                  << hwnd << ","  << endl;         cout << "},"  << endl;     }     return  TRUE; } void  GetHwndByTitle (wchar_t  *title)  {    cout << "["  << endl;     EnumWindows (EnumWindowsProc, (LPARAM)title);     cout << "]"  << endl; } 
获取窗口位置和大小 1 2 3 4 5 6 7 8 9 10 11 #include  <windows.h>  HWND hwnd; RECT rect; GetWindowRect (hwnd, &rect);cout << "窗口距离屏幕左上角的坐标:"  << endl; cout << rect.left << " "  << rect.top << endl; cout << "窗口的宽度:"  << endl; cout << rect.right - rect.left << endl; cout << "窗口的高度:"  << endl; cout << rect.bottom - rect.top << endl; 
命令行 命令行参数解析 参考  
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 #include  <unistd.h>  #include  <stdio.h>  #include  <stdlib.h>  int  main (int  argc, char  *argv[])  {    int  ch;     while  ((ch = getopt (argc, argv, "i:o:m:f:" )) != -1 )     {         switch  (ch)         {         case  'i' :             printf ("Input CP: %d\n" , atoi (optarg));             break ;         case  'o' :             printf ("Output CP: %d\n" , atoi (optarg));             break ;         case  'm' :             printf ("Mode: %s\n" , optarg);             break ;         case  'f' :             printf ("Func: %s\n" , optarg);         }     } } 
使用
1 2 3 4 5 6 main.exe -i 936 -o 65001 -m myMoudle -f myFunc 
Makefile 对于大型复杂应用,或者需要引入复杂依赖的应用,Makefile 是必不可少的。 下面是一个 Makefile 示例:
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 30 31 32 33 34 35 src = $(wildcard  ./src/*.cpp)  obj = $(patsubst  ./src/%.cpp, ./obj/%.o, $(src) )  src_path = ./src inc_path = ./include  lib_path = ./lib obj_path = ./obj out_path = ./output myArgs = -lstdc++ ALL: my.exe $(obj) : $(obj_path) /%.o: ${src_path}/%.cpp	gcc -c $<  -o $@  -I $(inc_path)  my.exe: $(obj)  	gcc $^  -o $(out_path) /$@  -L$(lib_path)  $(myArgs)  clean : 	-rm -rf $(obj)  $(out_path) /my.exe .PHONY : clean ALL 
构建项目 对于使用 MinGW 的 Windows 用户,其命令行工具为 mingw32-make.exe ,如果想要使用 make  命令来构建项目,可以在添加一个脚本命令文件作为别名, 这里使用 powershell 脚本作为参考,新建一个 make.pl1 文件,内容如下:
别名脚本文件可以放到 mingw32-make.exe  的同一目录下(建议), 也可以放到自己项目目录下
语法特性 指针和引用 指针和引用都用来解决间接访问数据的问题。 指针比较灵活,但是复杂不易理解,容易引入错误。 引用比较简单,但是没有指针的灵活。
这里的“引用”也叫“变量名引用” 
1 2 3 4 5 #include  <iostream>  void  modifyValue (int  *ptr)   {    *ptr = 100 ;  } 
1 2 3 4 5 #include  <iostream>  void  modifyValue (int  &ref)   {    ref = 100 ;  } 
Lambda 表达式 在 C++11 中引入 
Lambda 表达式的基本语法如下
1 [捕获列表](参数列表) -> 返回类型 { 函数体 } 
捕获列表:用于捕获外部变量,可以是值捕获(默认)、引用捕获(&)、指针捕获(*)。 参数列表:与普通函数的参数列表类似,可以是空。 返回类型:可以显式声明,也可以由编译器自动推导。 函数体:与普通函数的函数体类似。 1 2 3 4 5 6 #include  <iostream>  int  main ()   {    auto  add = [](int  a, int  b) { return  a + b; };     std::cout << "Sum: "  << add (1 , 2 ) << std::endl;  } 
1 2 3 4 5 6 7 #include  <iostream>  int  main ()   {    int  x = 10 , y = 5 ;     auto  add = [x, y]() { return  x + y; };     std::cout << "Sum: "  << add () << std::endl;  } 
1 2 3 4 5 6 7 8 9 #include  <iostream>  int  main ()   {    int  x = 10 , y = 5 ;     auto  add = [&x, &y]() { return  x + y; };     std::cout << "Sum: "  << add () << std::endl;      x = 20 ;     std::cout << "Sum: "  << add () << std::endl;  } 
1 2 3 4 5 6 7 8 9 10 #include  <iostream>  #include  <algorithm>  int  main ()   {    int  arr[] = {5 , 2 , 8 , 1 , 9 };     std::sort (arr, arr + 5 , [](int  a, int  b) { return  a > b; });     for  (int  i = 0 ; i < 5 ; i++) {         std::cout << arr[i] << " " ;      } } 
算法 异步回调 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 #include  <iostream>  #include  <thread>  #include  <functional>  void  async_operation (int  i, std::function<void (int )> callback)   {    std::thread ([i, callback] {         std::this_thread::sleep_for (std::chrono::seconds (1 ));         callback (i * i);     }).detach (); } int  main ()   {    for  (int  i = 0 ; i < 5 ; ++i) {         async_operation (i, [](int  result) {             std::cout << "Result: "  << result << std::endl;         });     }          std::this_thread::sleep_for (std::chrono::seconds (2 ));     return  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 #include  <atomic>  #include  <mutex>  #include  <condition_variable>  std::atomic<bool > flag{false }; std::mutex mtx; std::condition_variable cv; void  other_thread_logic ()   {         flag.store (true );     cv.notify_one ();  } void  wait_logic ()   {    std::unique_lock<std::mutex> lock (mtx)  ;     cv.wait (lock, [] { return  flag.load (); });                 } 
参考 Makefile 从入门到上手