成员指针是 C++ 中一个相对特殊的概念,它允许我们存储类成员(数据成员或成员函数)的指针。本文将详细介绍成员指针的概念、用法以及实际应用场景。
1. 成员指针的基本概念
成员指针与普通指针不同,它指向的是类的成员,而不是对象。成员指针的类型需要包含类名,因为成员指针需要知道它属于哪个类。
1.1 数据成员指针
cpp
class Person {
public:
std::string name;
int age;
};
// 声明成员指针
std::string Person::*namePtr = &Person::name;
int Person::*agePtr = &Person::age;
// 使用成员指针
Person p;
p.*namePtr = "张三"; // 等价于 p.name = "张三"
p.*agePtr = 25; // 等价于 p.age = 25
1.2 成员函数指针
cpp
class Calculator {
public:
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
};
// 声明成员函数指针
int (Calculator::*addPtr)(int, int) = &Calculator::add;
int (Calculator::*subtractPtr)(int, int) = &Calculator::subtract;
// 使用成员函数指针
Calculator calc;
Calculator* pCalc = &calc;
int result1 = (calc.*addPtr)(5, 3); // 使用对象调用
int result2 = (pCalc->*addPtr)(5, 3); // 使用指针调用
2. 静态成员指针与普通成员指针的对比
2.1 静态成员指针
cpp
class MathUtils {
public:
static int add(int a, int b) { return a + b; }
static int subtract(int a, int b) { return a - b; }
static double PI;
};
double MathUtils::PI = 3.14159;
// 静态成员函数指针
int (*staticAddPtr)(int, int) = &MathUtils::add;
int (*staticSubtractPtr)(int, int) = &MathUtils::subtract;
// 静态数据成员指针
double* staticPiPtr = &MathUtils::PI;
// 使用静态成员指针
int result = staticAddPtr(5, 3); // 直接调用,不需要对象
double pi = *staticPiPtr; // 直接访问,不需要对象
2.2 主要区别
-
语法差异
- 普通成员指针:
ReturnType (ClassName::*ptrName)(Params) = &ClassName::memberName
- 静态成员指针:
ReturnType (*ptrName)(Params) = &ClassName::memberName
- 普通成员指针:
-
使用方式
- 普通成员指针:需要通过对象或指针调用(使用
.*
或->*
操作符) - 静态成员指针:可以直接调用,不需要对象实例
- 普通成员指针:需要通过对象或指针调用(使用
-
内存模型
- 普通成员指针:存储的是成员在类中的偏移量
- 静态成员指针:存储的是实际的函数地址或变量地址
-
应用场景
- 普通成员指针:适用于需要访问特定对象成员的情况
- 静态成员指针:适用于全局函数或工具类的情况
3. 成员指针的实际应用
3.1 通用访问器
成员指针可以用来创建通用的成员访问器,这在需要统一处理多个成员时特别有用:
cpp
class Student {
public:
std::string name;
int score;
std::string address;
};
template<typename T>
void printMember(const Student& s, T Student::*member) {
std::cout << s.*member << std::endl;
}
// 使用示例
Student s{"李四", 95, "北京"};
printMember(s, &Student::name);
printMember(s, &Student::score);
printMember(s, &Student::address);
3.2 回调函数系统
成员指针在实现回调系统时非常有用:
cpp
class Button {
public:
using ClickHandler = void (Button::*)();
void setClickHandler(ClickHandler handler) {
clickHandler = handler;
}
void click() {
if (clickHandler) {
(this->*clickHandler)();
}
}
private:
ClickHandler clickHandler = nullptr;
void onDefaultClick() {
std::cout << "默认点击处理" << std::endl;
}
void onSpecialClick() {
std::cout << "特殊点击处理" << std::endl;
}
};
// 使用示例
Button btn;
btn.setClickHandler(&Button::onSpecialClick);
btn.click(); // 输出:特殊点击处理
3.3 使用 std::mem_fn
C++11 引入了 std::mem_fn
,它可以将成员函数指针转换为可调用对象:
cpp
#include <functional>
class Widget {
public:
void process() { std::cout << "处理中..." << std::endl; }
};
// 使用 std::mem_fn
auto processFn = std::mem_fn(&Widget::process);
Widget w;
processFn(w); // 调用 w.process()
4. 注意事项
- 成员指针不能指向静态成员
- 成员指针的类型必须完全匹配
- 使用成员指针时要注意访问权限
- 成员指针不能用于虚函数