安装并配置编译器 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 从入门到上手