C++类成员指针的实现示例
一、类成员指针的核心定位
类成员指针是C++中专门指向“类的成员”(而非具体对象的成员)的特殊指针,和普通指针的核心区别:
- 普通指针:直接指向内存中的某个地址(如变量、函数的入口地址);
- 成员指针:不直接指向内存地址,而是存储“成员在类中的偏移量”,必须绑定具体对象/对象指针后才能访问(因为类的成员属于对象,而非类本身)。
二、数据成员指针
1. 定义语法
指向类的非静态数据成员的指针,语法格式:
// 格式:类名::* 指针变量名
类型 类名::* 数据成员指针名 = &类名::数据成员名;
2. 完整示例(可直接运行)
#include <iostream>
using namespace std;
class Person {
public:
string name;
int age;
static int count; // 静态数据成员(特殊情况)
};
int Person::count = 0; // 静态成员初始化
int main() {
// 1. 定义并赋值:指向Person的age成员
int Person::*p_age = &Person::age;
// 指向Person的name成员
string Person::*p_name = &Person::name;
// 2. 绑定对象访问(用.*运算符)
Person p1{"Alice", 20};
cout << "Name: " << p1.*p_name << endl; // 输出Alice
cout << "Age: " << p1.*p_age << endl; // 输出20
// 3. 绑定对象指针访问(用->*运算符)
Person* p2 = &p1;
p2->*p_age = 21; // 修改age
cout << "New Age: " << p2->*p_age << endl; // 输出21
// 4. 静态数据成员指针(特殊:和普通指针一样)
int* p_count = &Person::count; // 无需Person::*,直接用普通指针
*p_count = 100;
cout << "Count: " << Person::count << endl; // 输出100
return 0;
}
3. 关键注意点
- 数据成员指针的类型必须严格匹配(如
int Person::*不能指向string Person::*); - 静态数据成员不属于对象,因此指向静态数据成员的指针是普通指针,而非成员指针;
- 空类/无数据成员的类,其数据成员指针的偏移量为0,但依然是合法的成员指针。
三、成员函数指针
1. 定义语法
指向类的非静态成员函数的指针,语法格式(需匹配返回值、参数列表、const/volatile限定):
// 格式:返回值 (类名::*)(参数列表) [const/volatile]
返回值 (类名::* 函数指针名)(参数列表) [const] = &类名::成员函数名;
2. 完整示例(含const成员函数)
#include <iostream>
#include <string>
using namespace std;
class Calculator {
public:
int add(int a, int b) { return a + b; }
int sub(int a, int b) const { return a - b; } // const成员函数
static int mul(int a, int b) { return a * b; } // 静态成员函数
};
int main() {
// 1. 指向非const成员函数
int (Calculator::*p_add)(int, int) = &Calculator::add;
// 2. 指向const成员函数(必须加const限定)
int (Calculator::*p_sub)(int, int) const = &Calculator::sub;
// 3. 绑定对象调用(.*运算符)
Calculator calc;
cout << "Add: " << (calc.*p_add)(10, 5) << endl; // 输出15
cout << "Sub: " << (calc.*p_sub)(10, 5) << endl; // 输出5
// 4. 绑定对象指针调用(->*运算符)
Calculator* p_calc = &calc;
cout << "Add via ptr: " << (p_calc->*p_add)(20, 3) << endl; // 输出23
// 5. 静态成员函数指针(特殊:和普通函数指针兼容)
int (*p_mul)(int, int) = &Calculator::mul;
cout << "Mul: " << p_mul(4, 5) << endl; // 输出20
return 0;
}
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及以上)
最经典的方式,将成员函数与对象绑定,生成可调用的函数对象:
#include <iostream>
#include <functional> // 必须包含
using namespace std;
class Printer {
public:
void print(string msg, int num) {
cout << msg << ": " << num << endl;
}
};
int main() {
Printer p;
// 绑定成员函数+对象,固定部分参数(也可留空参数用placeholders)
auto func = bind(&Printer::print, &p, "Number", placeholders::_1);
// 调用:只需传未绑定的参数
func(100); // 输出Number: 100
return 0;
}
2. 方式2:std::function适配
将成员函数指针+对象封装为std::function,适配通用可调用接口:
#include <iostream>
#include <functional>
using namespace std;
class Math {
public:
int square(int x) { return x * x; }
};
int main() {
Math m;
// 封装为std::function:参数列表要匹配成员函数(隐含this已绑定)
function<int(int)> f = bind(&Math::square, &m, placeholders::_1);
cout << f(5) << endl; // 输出25
// 也可直接用lambda(更简洁,见方式3)
function<int(int)> f2 = [&m](int x) { return m.square(x); };
cout << f2(6) << endl; // 输出36
return 0;
}
3. 方式3:lambda表达式包裹(推荐,C++11+)
最简洁、易读的方式,用lambda捕获对象/this,包裹成员函数调用:
#include <iostream>
#include <thread> // 用于演示线程调用成员函数
using namespace std;
class Worker {
public:
void work(int id) {
cout << "Worker " << id << " is running" << endl;
}
};
int main() {
Worker w;
// 1. 普通调用:lambda捕获对象
auto func = [&w]() { w.work(1); };
func(); // 输出Worker 1 is running
// 2. 线程调用成员函数(核心场景)
thread t([&w]() { w.work(2); });
t.join(); // 输出Worker 2 is running
// 3. 捕获this(成员函数内调用)
class Test {
public:
void call_self() {
auto f = [this]() { this->work(3); };
f(); // 输出Worker 3 is running
}
void work(int id) { cout << "Test Worker " << id << endl; }
};
Test tst;
tst.call_self();
return 0;
}
4. 典型场景:成员函数作为算法的回调
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Filter {
public:
bool is_even(int x) { return x % 2 == 0; }
};
int main() {
vector<int> nums = {1,2,3,4,5};
Filter f;
// 用lambda包裹成员函数,作为find_if的谓词
auto it = find_if(nums.begin(), nums.end(), [&f](int x) {
return f.is_even(x);
});
if (it != nums.end()) {
cout << "First even number: " << *it << endl; // 输出2
}
return 0;
}
五、核心对比:成员指针 vs 普通指针
| 特性 | 普通指针(如int*、void(*)(int)) | 类成员指针(如int A::、void (A::)(int)) |
|---|---|---|
| 指向目标 | 内存地址(变量/函数入口) | 类成员的偏移量(需绑定对象) |
| 大小 | 固定(64位=8字节) | 可能更大(如多继承场景=16字节) |
| 调用/访问方式 | 直接解引用(*p、p()) | 需绑定对象,用.* / ->* 运算符 |
| 静态成员适配 | 完全兼容 | 静态成员的指针等价于普通指针 |
| this指针 | 无 | 非静态成员函数指针隐含this,需绑定对象 |
总结
- 成员指针本质:存储类成员的偏移量,而非直接内存地址,必须绑定对象才能访问/调用;
- 语法关键:数据成员指针用
类名::*,成员函数指针需匹配返回值/参数/const限定,调用用.*/->*; - 可调用对象适配:优先用lambda表达式包裹成员函数(简洁无坑),其次用
std::bind,避免直接传递成员函数指针; - 特殊情况:静态成员的指针等价于普通指针,无需绑定对象即可使用。
到此这篇关于C++类成员指针的实现示例的文章就介绍到这了,更多相关C++类成员指针内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
visual studio 2022 编译出来的文件被删除并监视目录中的文件变更(示例详解)
这篇文章主要介绍了visual studio 2022 编译出来的文件被删除 并监视目录中的文件变更,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2022-08-08
C++ BoostAsyncSocket实现异步反弹通信的案例详解
这篇文章主要为大家详细介绍了C++ BoostAsyncSocket如何实现异步反弹通信,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的可以了解一下2023-03-03


最新评论