【C++】007、宏与inline的区别
一、根本区别宏是无脑的文本替换在预处理阶段机械替换不经过编译器语义检查inline是有脑的编译器建议本质是带类型检查的函数但最终是否内联展开由编译器根据复杂、优化级别决定二、四大核心区别对比宏函数#defineinline函数处理阶段预处理阶段纯文本替换不编译编译阶段编译器会尝试内联展开类型检查无严格的类型检查参数求值会多次求值存在副作用只求值一次调试能力无法调试可打断点调试作用域全局污染遵循C作用域规则代码展示多次求值的副作用#define SQUARE(x) ((x) * (x)) inline int square(int x) { return x * x; } int main() { int a 3; int b 3; int r1 SQUARE(a); // 展开: ((a) * (a)) a 变成了 5结果是 5*525UB结果取决于编译器 int r2 square(b); // 先计算 bb4传进去结果为 16。安全 }三、inline失效的四大场景1、编译器认为函数太复杂存在代码膨胀情况而进行拒绝场景函数体包含循环for/while递归switch分支过多或静态变量声明时编译器通常会自动忽略inline请求把它当做普通函数进行处理inline int factorial(int n) { return n 1 ? 1 : n * factorial(n - 1); // ❌ 递归编译器基本不会内联 } inline int complex_calc() { int sum 0; for (int i 0; i 1000; i) sum i; // 循环太长通常不会内联 return sum; }2、函数通过函数指针调用场景当把inline函数赋值给函数指针或者通过函数指针回调时编译器必须生成该函数的实际地址可执行实体此时内联无法展开inline void printHello() { std::cout Hello; } void invoke(void (*func)()) { func(); } int main() { void (*ptr)() printHello; // 获取了地址编译器被迫生成真实的函数体 invoke(ptr); // 这里调用的是函数指针无法内联 }3、虚函数调用动态多态场景通过基类指针或引用调用virtual inline函数时由于调用目标在运行时才能确定查虚表编译器无法再编译期展开内联函数class Base { public: virtual inline void foo() {} }; class Derived : public Base { public: void foo() override {} }; void test(Base* b) { b-foo(); // 运行时多态无法内联除非编译器能确定b的确切类型并去虚拟化 }4、构造、析构函数构造函数在头文件的类体内如果变量很多的话编译器会拒绝内联四、inline的底层本质inline对于编译器而言只是建议不是命令会根据情况有可能不会展开五、现代C替代宏函数替代普通宏函数使用 inline函数模板替代类型通用宏 使用auto 模板编译期常量计算宏 使用constepr