类的内存占用
需要预先了解: 类与对象 内存基础 多态 类的实例的内存占用 类的实例(对象)的内存占用,指的是 sizeof(类名) 的结果——它只统计数据成员和编译器为支持语言特性而插入的隐藏成员(如虚表指针),不包含静态成员变量和成员函数。先看一组真实测量(64 位平台): #include <cstdio> class Empty {}; // 空类 class WithInt { public: int x; }; class WithVirt { public: virtual void f() {} int x; }; class WithStatic { public: static int s; // 静态成员变量 int x; }; int WithStatic::s = 0; int main() { prin...
extern、static 与 inline 关键字总结
需要预先了解: 变量与常量 函数 内存基础 extern、static、inline 修饰变量时的区别 三个关键字修饰变量时,本质都在操纵声明与定义的关系和链接属性(linkage)。要理解它们的区别,先要弄清楚链接是怎么回事。 翻译单元与链接 一个 C++ 程序通常由多个 .cpp 文件组成。每个 .cpp 文件连同它 #include 的所有头文件,构成一个翻译单元(translation unit)。编译器逐个翻译单元独立编译,产出目标文件(.o),链接器(linker)再把所有目标文件合并成可执行文件。 每个翻译单元中定义的全局变量和函数,都会在目标文件里生成一个符号(symbol)——它是链接器用来识别"哪个名字对应哪块地址"的索引。链接器需要回答一个关键问题:a.cpp 里用到了 global,该去哪里找它的定义?答案取决于符号的链接属性: 外部链接(external linkage):符号对其他翻译单元可见,链接器可以在整个程序范围内解析它 内部链接(internal linkage):符号只在本翻译单元内可见,...
类型转换
需要预先了解: 变量与常量 指针 类与对象 多态 隐式类型转换 隐式类型转换(implicit conversion)由编译器自动完成,不需要程序员写出任何转换语法。它发生在赋值、传参、表达式求值等场景中,正确使用时十分自然,但某些情况下会悄悄引入错误。 数值转换与整型提升 小整型参与算术运算时,会先提升为 int,这叫整型提升(integral promotion): char c = 'A'; int n = c; // char 提升为 int,n = 65 short s = 100; int sum = s + 1; // s 先提升为 int 再参与加法 从窄到宽的数值转换是安全的:int 转 long、float 转 double 都不丢失信息。但从宽到窄的转换会截断或丢失精度,比如 double 转 int 直接丢掉小数部分: double d = 3.99; int x = d; // x = 3,小数部分被丢弃 浮点转整数的截断方向是向零取整(truncation),不是四舍五...
Lambda表达式
需要预先了解: 函数 lambda 表达式 通过使用lambda表达式可以在代码中就地定义短小的函数逻辑,同时能捕获外层作用域的变量作为内部状态,其核心语法如下: [捕获列表](参数列表) { 函数体 } 例如: []() { std::cout << "this is lambda!" << std::endl; } 1. 无参数时,括号可以省略 [] { std::cout << "this is lambda!" << std::endl; } 2. 可以在函数体后紧跟一个括号,表示直接调用该匿名函数 [] { std::cout << "this is lambda!" << std::endl; }(); lambda 表达式的捕获方式 值捕获 值捕获:捕获变量的副本,在lambda定义时完成拷贝。 显式捕获:写法为[变量名1, 变量名2, ...],用于显式捕获外界的变量 隐式捕获:写法为[=...
函数指针与std::function
需要预先了解: 函数 指针 函数指针 函数的地址 函数的机器指令存储在内存的.text段里,每个函数对应.text段中的一段连续的机器指令序列,其起始地址(即函数第一条指令所在的内存地址)就是这个函数的地址。 函数指针 函数指针是指向函数的指针类型,它存储函数的入口地址,通过这个地址可以间接调用对应的函数。 其声明语法为返回值类型 (*函数指针名)(参数列表): int add(int a, int b); int (*p)(int, int); // p 是类型为 int (*)(int, int)的函数的指针 函数名也可以隐式转换为函数的入口地址 因此有以下两种等价的写法: int (*p)(int, int) = add; int (*p)(int, int) = &add; 调用函数指针也有等价的两种写法: std::cout << p(1, 2) << std::endl; std::cout << (*p)(1, 2) << std::endl; std::function std:...
智能指针与RAII
需要预先了解: 指针 原始指针的缺点 原始指针/裸指针:如int* p,需要手动管理内存,容易造成以下问题: 1. 忘记delete/free会导致内存泄露: // 局部变量 ptr 指向的堆内存未释放,造成内存泄露 void func { Type* ptr = new Type(); ptr->do_something(); } 2.使用释放后的悬挂指针,导致未定义行为: Type* ptr = new Type(); delete ptr; ptr->do_something(); 3. 重复释放同一块内存,导致未定义行为或程序崩溃 Type* ptr = new Type(); delete ptr; delete ptr; // 重复释放同一块内存 RAII(Resource Acquisition Is Initialization) RAII(直译为“资源获取即初始化”):在 对象构造阶段获取资源,在 对象析构阶段释放资源;依靠 C++ 对象自动生命周期管理(离开作用域自动调用析构),保证资源一定会被释...
移动语义与std::move
需要预先学习: 引用与左右值 移动语义 移动语义(Move Semantics)是 C++11 引入的一项重要特性,它的核心思想是:当你把一个对象的资源"转移"到另一个对象时,不需要做深拷贝,而是直接把指针/句柄"偷"过来,让原对象变成一个空壳。这样可以避免大量不必要的拷贝开销,大幅提升性能。 拷贝的缺点 在传统 C++ 中,对象的传递依赖拷贝。对于管理堆内存的类(如 std::string、std::vector),拷贝意味着: 分配新的堆内存 逐字节复制原对象的数据 原对象和目标对象各自拥有独立的内存 #include <cstring> #include <iostream> class MyString { public: MyString(const char* str) { _size = std::strlen(str); _data = new char[_size + 1]; std::strcpy(_da...
联合与std::variant
需要预先了解: 内存基础 变量与常量 联合(Union) 联合(union) 是一种特殊的数据类型,它允许在同一块内存空间中存储不同类型的数据,但同一时刻只能有一个成员是有效的。定义语法与 struct 类似,只是把关键字换为 union: union Data { int i; double d; char c; }; 使用时像结构体一样访问成员,但所有成员共享同一块内存: #include <iostream> int main() { Data u; u.i = 42; // 写入 int std::cout << u.i << std::endl; // 输出: 42 u.d = 3.14; // 写入 double——覆盖了原来的 int std::cout << u.d << std::endl; /...
模版基础
需要预先学习: 函数 函数模版 函数模版(function template)是一份"函数蓝图"——你只需要写一次逻辑,编译器就能为不同的类型自动生成对应的函数版本。 基础语法 函数模版的声明以 template <typename T> 开头,其中 T 是一个模版参数(template parameter),代表一个待定的类型。使用时,编译器会根据你传入的实参类型推导出 T 的具体类型。 #include <iostream> template <typename T> T max(T a, T b) { return (a > b) ? a : b; } int main() { std::cout << max(3, 7) << '\n'; // T 推导为 int,输出 7 std::cout << max(3.14, 2.71) << '\n'; // T 推导为 double,输出 3.1...
可变长参数模版
需要预先学习: 模版基础 可变长参数模版 普通模版的参数数量是固定的——template <typename T1, typename T2> 只能接受两个类型。而可变长参数模版(variadic template)打破了这一限制:可以写出接受任意数量、任意类型参数的模版。 这是 C++11 引入的特性,std::tuple、std::make_shared、std::thread 的构造函数等大量标准库组件都是基于它实现的。 基础语法 可变长参数模版的核心是参数包(parameter pack)。用 typename... Args 声明一个类型参数包,用 Args... args 声明一个函数参数包: template <typename... Args> void print(Args... args) { // args 是一个参数包,包含调用时传入的全部实参 } Args 是一个类型参数包(type parameter pack),代表零个或多个类型;args 是一个函数参数包(function parame...


