C++类成员指针的实现示例
一、类成员指针的核心定位
类成员指针是C++中专门指向“类的成员”(而非具体对象的成员)的特殊指针,和普通指针的核心区别:
- 普通指针:直接指向内存中的某个地址(如变量、函数的入口地址);
- 成员指针:不直接指向内存地址,而是存储“成员在类中的偏移量”,必须绑定具体对象/对象指针后才能访问(因为类的成员属于对象,而非类本身)。
二、数据成员指针
1. 定义语法
指向类的非静态数据成员的指针,语法格式:
// 格式:类名::* 指针变量名
类型 类名::* 数据成员指针名 = &类名::数据成员名;
2. 完整示例(可直接运行)
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 |
|
3. 关键注意点
- 数据成员指针的类型必须严格匹配(如
int Person::*不能指向string Person::*); - 静态数据成员不属于对象,因此指向静态数据成员的指针是普通指针,而非成员指针;
- 空类/无数据成员的类,其数据成员指针的偏移量为0,但依然是合法的成员指针。
三、成员函数指针
1. 定义语法
指向类的非静态成员函数的指针,语法格式(需匹配返回值、参数列表、const/volatile限定):
// 格式:返回值 (类名::*)(参数列表) [const/volatile]
返回值 (类名::* 函数指针名)(参数列表) [const] = &类名::成员函数名;
2. 完整示例(含const成员函数)
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 |
|
3. 关键注意点
- 成员函数指针的括号不可省略:
(calc.*p_add)(10,5)不能写成calc.*p_add(10,5)(运算符优先级问题,.和->*优先级低于()); - const成员函数的指针必须加
const限定,否则不匹配(如int (Calculator::*)(int,int) const不能赋值给int (Calculator::*)(int,int)); - 成员函数隐含
this指针,因此必须绑定对象才能调用(静态成员函数无this,故无需绑定); - 成员函数指针的大小可能大于普通指针(如64位系统可能占16字节),因为需要存储函数地址+this调整信息(多继承场景)。
四、将成员函数用作可调用对象
成员函数指针本身不能直接作为“无上下文的可调用对象”(如传给std::thread、std::function、std::for_each等),必须绑定对象/this指针,常用3种方式:
1. 方式1:std::bind(C++11及以上)
最经典的方式,将成员函数与对象绑定,生成可调用的函数对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
2. 方式2:std::function适配
将成员函数指针+对象封装为std::function,适配通用可调用接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
3. 方式3:lambda表达式包裹(推荐,C++11+)
最简洁、易读的方式,用lambda捕获对象/this,包裹成员函数调用:
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 |
|
4. 典型场景:成员函数作为算法的回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
五、核心对比:成员指针 vs 普通指针
| 特性 | 普通指针(如int*、void(*)(int)) | 类成员指针(如int A::、void (A::)(int)) |
|---|---|---|
| 指向目标 | 内存地址(变量/函数入口) | 类成员的偏移量(需绑定对象) |
| 大小 | 固定(64位=8字节) | 可能更大(如多继承场景=16字节) |
| 调用/访问方式 | 直接解引用(*p、p()) | 需绑定对象,用.* / ->* 运算符 |
| 静态成员适配 | 完全兼容 | 静态成员的指针等价于普通指针 |
| this指针 | 无 | 非静态成员函数指针隐含this,需绑定对象 |
总结
- 成员指针本质:存储类成员的偏移量,而非直接内存地址,必须绑定对象才能访问/调用;
- 语法关键:数据成员指针用
类名::*,成员函数指针需匹配返回值/参数/const限定,调用用.*/->*; - 可调用对象适配:优先用lambda表达式包裹成员函数(简洁无坑),其次用
std::bind,避免直接传递成员函数指针; - 特殊情况:静态成员的指针等价于普通指针,无需绑定对象即可使用。
