类的内部机制
约 1355 个字 239 行代码 预计阅读时间 14 分钟
类内部实现机制
本章深入探讨C++类的内部实现机制,包括函数重载、委托构造函数、默认参数、内联函数等高级特性,这些是实现高效C++类设计的重要基础。
对象与类
- 表示事物、事件或概念的实例
- 在运行时响应消息
- 代表特定实例(例如:这只猫)
- 定义实例的属性和行为
- 在C++中作为类型存在
- 定义共同特征(例如:猫这个种类)
函数重载
-
基本概念
函数重载是指用同一个函数名定义多个功能类似但参数列表不同的函数:
void print(char* str, int width); // #1 void print(double d, int width); // #2 void print(long l, int width); // #3 void print(int i, int width); // #4 void print(char* str); // #5 // 调用时根据参数类型自动选择正确的函数 print("Pancakes", 15); // 调用 #1 print("Syrup"); // 调用 #5 print(1999.0, 10); // 调用 #2 print(1999, 12); // 调用 #4 print(1999L, 15); // 调用 #3
当没有完全匹配的函数时,编译器会尝试进行类型转换:
-
构造函数重载
类可以有多个构造函数,用于不同的初始化场景:
多个构造函数可能包含重复代码:
class ClassC { public: int max, min, middle; ClassC() {} ClassC(int my_max) { max = my_max > 0 ? my_max : 10; } ClassC(int my_max, int my_min) { max = my_max > 0 ? my_max : 10; min = my_min > 0 && my_min < max ? my_min : 1; } ClassC(int my_max, int my_min, int my_middle) { max = my_max > 0 ? my_max : 10; min = my_min > 0 && my_min < max ? my_min : 1; middle = my_middle < max && my_middle > min ? my_middle : 5; } };
委托构造函数
-
基本用法
委托构造函数(Delegating Constructor)是C++11引入的特性,允许一个构造函数调用同一个类的另一个构造函数:
class ClassC { public: int max, min, middle; // 基础构造函数 ClassC(int my_max) { max = my_max > 0 ? my_max : 10; } // 委托给第一个构造函数,然后执行自己的代码 ClassC(int my_max, int my_min) : ClassC(my_max) { min = my_min > 0 && my_min < max ? my_min : 1; } // 委托给第二个构造函数,然后执行自己的代码 ClassC(int my_max, int my_min, int my_middle) : ClassC(my_max, my_min) { middle = my_middle < max && my_middle > min ? my_middle : 5; } };
- 目标构造函数(被委托的构造函数)先执行
- 委托构造函数后执行
- 构造函数不能同时使用委托和其他初始化列表
-
委托构造好处
多个构造函数通常需要执行相似的初始化代码,委托构造避免了这种重复:
// 不好的做法:重复代码 class Widget { int x, y, z; public: Widget() { x = 0; y = 0; z = 0; initRest(); } Widget(int a) { x = a; y = 0; z = 0; initRest(); } Widget(int a, int b) { x = a; y = b; z = 0; initRest(); } void initRest() { /* 共同初始化代码 */ } }; // 好的做法:委托构造 class Widget { int x, y, z; public: Widget() : Widget(0, 0, 0) {} Widget(int a) : Widget(a, 0, 0) {} Widget(int a, int b) : Widget(a, b, 0) {} Widget(int a, int b, int c) : x(a), y(b), z(c) { // 共同初始化代码 } };
委托构造确保所有构造路径使用相同的初始化顺序:
- 委托构造不能形成循环链(会导致编译错误)
- 被委托的构造函数的初始化列表和函数体都会被执行
默认参数
默认参数是在函数声明中给参数提供的默认值,如果调用时没有提供这些参数,编译器会自动使用默认值:
-
默认参数必须从右向左添加(右边的所有参数都必须有默认值):
-
默认参数在声明处指定,而不是在定义处:
默认参数可以用于构造函数,如果所有参数都有默认值,这个构造函数也是默认构造函数:
class Box {
private:
double length, width, height;
public:
// 这是一个带默认参数的构造函数
// 也是一个默认构造函数
Box(double l = 1.0, double w = 1.0, double h = 1.0)
: length(l), width(w), height(h) {}
};
// 创建Box对象的不同方式
Box b1; // 使用所有默认值: (1.0, 1.0, 1.0)
Box b2(2.0); // (2.0, 1.0, 1.0)
Box b3(2.0, 3.0); // (2.0, 3.0, 1.0)
内联函数
-
内联基础
内联函数是一种特殊的函数,编译器会尝试在调用处展开函数体,而不是生成函数调用,从而减少函数调用的开销:
函数调用涉及多个步骤,会产生开销:
- 压入参数
- 保存返回地址
- 跳转到函数地址
- 为局部变量分配空间
- 执行函数体
- 准备返回值
- 跳回调用点
- 弹出之前压入的值
-
内联使用
内联函数的定义必须对所有调用点可见,所以通常放在头文件中:
// 在头文件中定义内联函数 // mymath.h #ifndef MYMATH_H #define MYMATH_H inline int square(int x) { return x * x; } #endif
- 不必担心多重定义问题,内联函数的定义可以在多个翻译单元中出现
- 内联只是对编译器的建议,编译器可能不接受这个建议
在类定义内部直接定义的成员函数自动成为内联函数:
优点:
- 减少函数调用开销,提高性能
- 编译器可能进行更多优化
- 比宏更安全(类型检查,作用域规则)
缺点:
- 增加代码体积
- 不适合大型或复杂函数
- 不支持递归
- 调试时可能更困难
-
内联变量(C++17)
C++17引入了内联变量,允许在头文件中定义变量而不会导致链接错误:
使用内联初始化类的静态成员变量:
特性 inline weak 主要用途 优化函数调用,避免多重定义 允许符号覆盖,防止链接冲突 适用对象 主要用于函数和变量 函数、变量、对象 符号管理 编译器生成函数的多个副本并去重 多个弱符号可共存,链接器选择最强的 链接行为 多个翻译单元定义同一内联函数不会导致错误 存在多个弱符号不会导致错误 优化重点 减少函数调用开销提高性能 提供备用实现或插件的可选符号
内联使用建议
- 适合内联:小函数(2-3行)、频繁调用的函数(如循环内部)
- 不适合内联:大函数(超过20行)、递归函数、复杂逻辑函数
- 实用建议:让编译器决定,使用编译器优化选项,只对关键路径上的小函数使用inline