指向数据成员的指针
一个指向非静态成员对象m
的指针,它是C
类的成员,可以用表达式&C::m
准确地初始化。表达式例如&(C::m)
或者在成员函数里面使用&m
都不是指向数据成员的指针。&(C::m)
是取C
类里面的静态成员m
地址,而后面一种则是取具体对象里面的变量m
的地址,两者都是普通指针。
指向数据成员的指针可以用两种操作符调用:operator.*
和operator->*
前者用于对象或者引用,后者用于对象指针
1struct C { int m; };
2
3int main()
4{
5 int C::* p = &C::m; // pointer to data member m of class C
6 C c = {7};
7 std::cout << c.*p << '\n'; // prints 7
8 C* cp = &c;
9 cp->m = 10;
10 std::cout << cp->*p << '\n'; // prints 10
11}
一个可访问的非明确的非虚拟基类的数据成员的指针可以隐式转换转换为指向派生类的相同数据成员的指针。
1struct Base { int m; };
2struct Derived : Base {};
3
4int main()
5{
6 int Base::* bp = &Base::m;
7 int Derived::* dp = bp;
8 Derived d;
9 d.m = 1;
10 std::cout << d.*dp << ' ' << d.*bp << '\n'; // prints 1 1
11}
相反方向的转换,从派生类的数据成员的指针到非虚拟基类的数据成员的指针,允许使用static_cast和explicit cast,即使基类没有该成员(但最派生类有,当指针被用于访问时)。
1struct Base {};
2struct Derived : Base { int m; };
3
4int main()
5{
6 int Derived::* dp = &Derived::m;
7 int Base::* bp = static_cast<int Base::*>(dp);
8
9 Derived d;
10 d.m = 7;
11 std::cout << d.*bp << '\n'; // okay: prints 7
12
13 Base b;
14 std::cout << b.*bp << '\n'; // undefined behavior
15}
指针到成员的指针类型可以是指针到成员本身:指针到成员可以是多级的,并且可以在每一级以不同的方式进行cv限定。指针和指针到成员的混合多级组合也是允许的。
1struct A
2{
3 int m;
4 // const pointer to non-const member
5 int A::* const p;
6};
7
8int main()
9{
10 // non-const pointer to data member which is a const pointer to non-const member
11 int A::* const A::* p1 = &A::p;
12
13 const A a = {1, &A::m};
14 std::cout << a.*(a.*p1) << '\n'; // prints 1
15
16 // regular non-const pointer to a const pointer-to-member
17 int A::* const* p2 = &a.p;
18 std::cout << a.**p2 << '\n'; // prints 1
19}
成员函数的指针
一个指向非静态成员函数f
的指针,它是C
类的成员,可以用表达式&C::f
来初始化。表达式例如&(C::f)
或者在成员函数里面使用&f
都不是指向成员函数的指针。同理,&(C::f)
是去静态函数指针,&f
是取该类里面的函数指针
指向成员函数的指针可以用两种操作符调用:operator.*
和operator->*
前者用于对象或者引用,后者用于对象指针
1struct C
2{
3 void f(int n) { std::cout << n << '\n'; }
4};
5
6int main()
7{
8 void (C::* p)(int) = &C::f; // pointer to member function f of class C
9 C c;
10 (c.*p)(1); // prints 1
11 C* cp = &c;
12 (cp->*p)(2); // prints 2
13}
基类成员函数的指针可以隐式转换为派生类同一成员函数的指针
1struct Base
2{
3 void f(int n) { std::cout << n << '\n'; }
4};
5struct Derived : Base {};
6
7int main()
8{
9 void (Base::* bp)(int) = &Base::f;
10 void (Derived::* dp)(int) = bp;
11 Derived d;
12 (d.*dp)(1);
13 (d.*bp)(2);
14}
相反方向的转换,从一个派生类的成员函数的指针到一个明确的非虚拟基类的成员函数的指针,允许使用static_cast和explicit cast,即使基类没有该成员函数(但最派生类有,当指针被用于访问时)。
1struct Base {};
2struct Derived : Base
3{
4 void f(int n) { std::cout << n << '\n'; }
5};
6
7int main()
8{
9 void (Derived::* dp)(int) = &Derived::f;
10 void (Base::* bp)(int) = static_cast<void (Base::*)(int)>(dp);
11
12 Derived d;
13 (d.*bp)(1); // okay: prints 1
14
15 Base b;
16 (b.*bp)(2); // undefined behavior
17}
成员函数的指针可以作为回调或作为函数对象使用,通常在应用了std::mem_fn
或者std::bind
1#include <iostream>
2#include <string>
3#include <algorithm>
4#include <functional>
5
6int main()
7{
8 std::vector<std::string> v = {"a", "ab", "abc"};
9 std::vector<std::size_t> l;
10 transform(v.begin(), v.end(), std::back_inserter(l),
11 std::mem_fn(&std::string::size));
12 for(std::size_t n : l)
13 std::cout << n << ' ';
14}
测试例子
1#include <string>
2#include <iostream>
3#include <tuple>
4#include <type_traits>
5
6enum class Gender
7{
8 kMale,
9 kFemale,
10 kOther,
11};
12
13struct Person
14{
15 std::string name;
16 int age;
17 Gender gender;
18
19 static int median_age;
20
21 void print()
22 {
23 std::cout << name << " " << age << " " << static_cast<int>(gender) << std::endl;
24 }
25};
26
27int Person::median_age;
28
29template<typename OBJECT_TYPE, typename ELEMENT_T>
30void setStructElement(OBJECT_TYPE &obj, ELEMENT_T OBJECT_TYPE::* key, ELEMENT_T v)
31{
32 obj.*key = v;
33}
34
35// 模板测试调用函数指针
36template<typename OBJECT_TYPE, typename ELEMENT_T>
37void call(OBJECT_TYPE &obj, ELEMENT_T func)
38{
39 (obj.*func)();
40}
41
42template<class T>
43struct remove_const
44{
45 typedef T type;
46};
47template<class T>
48struct remove_const<const T>
49{
50 typedef T type;
51};
52
53// 根据成员指针获取成员类型的模板
54template<typename T>
55struct get_member_type
56{
57
58};
59
60template<class T, class U>
61struct get_member_type<T U::*>
62{
63 using value = T;
64};
65
66template<typename ST, typename ...Args>
67auto structToTuple(const ST &st, Args...args)
68{
69 return std::make_tuple(st.*args...);
70}
71
72// 结构体转匿名结构体
73template<typename ST, typename ...Args>
74struct structToTupleType
75{
76 using value = std::tuple<get_member_type<Args...>>;
77};
78
79template<typename ST, typename ...Args>
80auto get_divider(Args...args)
81{
82 return std::make_tuple(args...);
83}
84
85int main()
86{
87 Gender Person::* gp = &Person::gender;
88 Person person;
89 Person *pp = &person;
90 person.*gp = Gender::kMale;
91 pp->*gp = Gender::kOther;
92 setStructElement(person, gp, Gender::kFemale);
93 auto fp = &Person::print;
94 (person.*fp)();
95 call(person, fp);
96 std::cout << &(Person::median_age);
97 auto tuple = structToTuple(person, &Person::name, &Person::age, &Person::gender);
98}