C++类成员指针

指向数据成员的指针

一个指向非静态成员对象m的指针,它是C类的成员,可以用表达式&C::m准确地初始化。表达式例如&(C::m)或者在成员函数里面使用&m都不是指向数据成员的指针。&(C::m)是取C类里面的静态成员m地址,而后面一种则是取具体对象里面的变量m的地址,两者都是普通指针。

指向数据成员的指针可以用两种操作符调用:operator.*operator->*前者用于对象或者引用,后者用于对象指针

1
2
3
4
5
6
7
8
9
10
11
struct C { int m; };

int main()
{
int C::* p = &C::m; // pointer to data member m of class C
C c = {7};
std::cout << c.*p << '\n'; // prints 7
C* cp = &c;
cp->m = 10;
std::cout << cp->*p << '\n'; // prints 10
}

一个可访问的非明确的非虚拟基类的数据成员的指针可以隐式转换转换为指向派生类的相同数据成员的指针。

1
2
3
4
5
6
7
8
9
10
11
struct Base { int m; };
struct Derived : Base {};

int main()
{
int Base::* bp = &Base::m;
int Derived::* dp = bp;
Derived d;
d.m = 1;
std::cout << d.*dp << ' ' << d.*bp << '\n'; // prints 1 1
}

相反方向的转换,从派生类的数据成员的指针到非虚拟基类的数据成员的指针,允许使用static_cast和explicit cast,即使基类没有该成员(但最派生类有,当指针被用于访问时)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Base {};
struct Derived : Base { int m; };

int main()
{
int Derived::* dp = &Derived::m;
int Base::* bp = static_cast<int Base::*>(dp);

Derived d;
d.m = 7;
std::cout << d.*bp << '\n'; // okay: prints 7

Base b;
std::cout << b.*bp << '\n'; // undefined behavior
}

指针到成员的指针类型可以是指针到成员本身:指针到成员可以是多级的,并且可以在每一级以不同的方式进行cv限定。指针和指针到成员的混合多级组合也是允许的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct A
{
int m;
// const pointer to non-const member
int A::* const p;
};

int main()
{
// non-const pointer to data member which is a const pointer to non-const member
int A::* const A::* p1 = &A::p;

const A a = {1, &A::m};
std::cout << a.*(a.*p1) << '\n'; // prints 1

// regular non-const pointer to a const pointer-to-member
int A::* const* p2 = &a.p;
std::cout << a.**p2 << '\n'; // prints 1
}

成员函数的指针

一个指向非静态成员函数f的指针,它是C类的成员,可以用表达式&C::f来初始化。表达式例如&(C::f)或者在成员函数里面使用&f都不是指向成员函数的指针。同理,&(C::f)是去静态函数指针,&f是取该类里面的函数指针

指向成员函数的指针可以用两种操作符调用:operator.*operator->*前者用于对象或者引用,后者用于对象指针

1
2
3
4
5
6
7
8
9
10
11
12
13
struct C
{
void f(int n) { std::cout << n << '\n'; }
};

int main()
{
void (C::* p)(int) = &C::f; // pointer to member function f of class C
C c;
(c.*p)(1); // prints 1
C* cp = &c;
(cp->*p)(2); // prints 2
}

基类成员函数的指针可以隐式转换为派生类同一成员函数的指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct Base
{
void f(int n) { std::cout << n << '\n'; }
};
struct Derived : Base {};

int main()
{
void (Base::* bp)(int) = &Base::f;
void (Derived::* dp)(int) = bp;
Derived d;
(d.*dp)(1);
(d.*bp)(2);
}

相反方向的转换,从一个派生类的成员函数的指针到一个明确的非虚拟基类的成员函数的指针,允许使用static_cast和explicit cast,即使基类没有该成员函数(但最派生类有,当指针被用于访问时)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct Base {};
struct Derived : Base
{
void f(int n) { std::cout << n << '\n'; }
};

int main()
{
void (Derived::* dp)(int) = &Derived::f;
void (Base::* bp)(int) = static_cast<void (Base::*)(int)>(dp);

Derived d;
(d.*bp)(1); // okay: prints 1

Base b;
(b.*bp)(2); // undefined behavior
}

成员函数的指针可以作为回调或作为函数对象使用,通常在应用了std::mem_fn或者std::bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>
#include <algorithm>
#include <functional>

int main()
{
std::vector<std::string> v = {"a", "ab", "abc"};
std::vector<std::size_t> l;
transform(v.begin(), v.end(), std::back_inserter(l),
std::mem_fn(&std::string::size));
for(std::size_t n : l)
std::cout << n << ' ';
}

测试例子

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <string>
#include <iostream>
#include <tuple>
#include <type_traits>

enum class Gender
{
kMale,
kFemale,
kOther,
};

struct Person
{
std::string name;
int age;
Gender gender;

static int median_age;

void print()
{
std::cout << name << " " << age << " " << static_cast<int>(gender) << std::endl;
}
};

int Person::median_age;

template<typename OBJECT_TYPE, typename ELEMENT_T>
void setStructElement(OBJECT_TYPE &obj, ELEMENT_T OBJECT_TYPE::* key, ELEMENT_T v)
{
obj.*key = v;
}

// 模板测试调用函数指针
template<typename OBJECT_TYPE, typename ELEMENT_T>
void call(OBJECT_TYPE &obj, ELEMENT_T func)
{
(obj.*func)();
}

template<class T>
struct remove_const
{
typedef T type;
};
template<class T>
struct remove_const<const T>
{
typedef T type;
};

// 根据成员指针获取成员类型的模板
template<typename T>
struct get_member_type
{

};

template<class T, class U>
struct get_member_type<T U::*>
{
using value = T;
};

template<typename ST, typename ...Args>
auto structToTuple(const ST &st, Args...args)
{
return std::make_tuple(st.*args...);
}

// 结构体转匿名结构体
template<typename ST, typename ...Args>
struct structToTupleType
{
using value = std::tuple<get_member_type<Args...>>;
};

template<typename ST, typename ...Args>
auto get_divider(Args...args)
{
return std::make_tuple(args...);
}

int main()
{
Gender Person::* gp = &Person::gender;
Person person;
Person *pp = &person;
person.*gp = Gender::kMale;
pp->*gp = Gender::kOther;
setStructElement(person, gp, Gender::kFemale);
auto fp = &Person::print;
(person.*fp)();
call(person, fp);
std::cout << &(Person::median_age);
auto tuple = structToTuple(person, &Person::name, &Person::age, &Person::gender);
}

C++类成员指针
https://www.helywin.com/posts/20220623042316/
作者
helywin
发布于
2022年6月23日
许可协议