访问c++类的私有成员

2026-06-30 01:30:00

访问c++类的私有成员

1 目标

近期需要对代码进行单测覆盖,期望不改动代码仓库的情况下,单测有足够多的灵活度,直接根据历史覆盖行数,设计出全覆盖的单测。因此,访问类的私有成员变量和函数必不可少。然后,c++类本身设计为对外部访问封闭(friend class or function也是要改动代码,放弃),需要调研一下访问私有的一下trait

2 方案1

#define private public

#define protected public

这两行代码,在编译预处理阶段,将private和protected关键字视为marco,替换为public。由于杀伤力大,仅能定义在cpp中,且依附在需要打开的h之前

3 方案2

c++标准仅在一种情况下支持,在类的外部访问私有成员变量(注意是类,而非类实例),那就是显示模板实例(explicit template instantiations)。

参考代码:二者原理差不多

https://github.com/martong/access_private

https://gist.github.com/dabrahams/1528856

下面是一些用法和分析:

1 #include "AccessPrivate2.h"

2

3 class A {

4 public:

5 A() {}

6 int GetA() const {return ia;}

7 protected:

8 int ib{0};

9 private:

10 int ia {0};

11 };

12

13 ACCESS_PRIVATE_FIELD(A, int, ia);

14

15 int main() {

16 A a;

17 auto& ia = access_private::ia(a);

18 ia = 3;

19 return 0;

20 }

对代码进行预编译展开后,核心代码片段为:

1 # 8 "./AccessPrivate2.h" 2

2 # 21 "./AccessPrivate2.h"

3 namespace {

4 namespace private_access_detail {

5 template

6 struct private_access {

7 friend PtrType get(TagType) { return PtrValue; }

8 };

9 }

10 }

11 # 4 "main.cpp" 2

12

13

14 class A {

15 public:

16 A() {}

17 int GetA() const {return ia;}

18 protected:

19 int ib{0};

20 private:

21 int ia {0};

22 };

23

24 namespace {

25 namespace private_access_detail {

26 struct PrivateAccessTag0 {};

27 template struct private_access<__decltype(&A::ia), &A::ia, PrivateAccessTag0>;

28 using Alias_PrivateAccessTag0 = int;

29 using PtrType_PrivateAccessTag0 = Alias_PrivateAccessTag0 A::*;

30 PtrType_PrivateAccessTag0 get(PrivateAccessTag0);

31 }

32 }

33

34 namespace {

35 namespace access_private {

36 int &ia(A &&t) { return t.*get(private_access_detail::PrivateAccessTag0{}); }

37 int &ia(A &t) { return t.*get(private_access_detail::PrivateAccessTag0{}); }

38 using XPrivateAccessTag0 = int;

39 using YPrivateAccessTag0 = const XPrivateAccessTag0;

40 YPrivateAccessTag0 & ia(const A &t) {

41 return t.*get(private_access_detail::PrivateAccessTag0{});

42 }

43 }

44 };

45

46 int main() {

47 A a;

48 auto& ia = access_private::ia(a);

49 ia = 3;

50 return 0;

51 }

再对名空间化简,得到最简可编译运行代码

1 //#include "Test.h"

2 //#include "AccessPrivate2.h"

3

4 class A {

5 public:

6 A() {}

7 int GetA() const {return ia;}

8 protected:

9 int ib{0};

10 private:

11 int ia {0};

12 };

13

14 namespace access_private {

15 // step1: global template define。

16 template

17 struct private_access {

18 // friend function means it is not a part of this struct, but it can use template params, amazing

19 friend PtrType get() { return PtrValue; }

20 };

21

22 // step2: explicit template instantiations : define by class name and member name

23 template struct private_access<__decltype(&A::ia), &A::ia>;

24 int A::* get();

25

26 // step3: define function instance

27 int &ia(A &&t) { return t.* get(); }

28 int &ia(A &t) { return t.* get(); }

29 const int & ia(const A &t) {

30 return t.* get();

31 }

32 }

33 //ACCESS_PRIVATE_FIELD(A, int, ia);

34

35 int main() {

36 A a;

37 auto& ia = access_private::ia(a);

38 ia = 3;

39 return 0;

40 }

可以看出来,替换分成3段,

第一段:全局的模版声明。这里面有个trait,在template struct里面仅定义了一个friend function,意味着这个函数非struct的一部分,但是可以享受到struct的模板参数,这个设计比较巧妙!

第二段:显示模板实例,c++里仅能访问类私有变量的地方。注意是class type的私有变量引用(偏移地址)

第三段:访问函数实现。参数传递class instantiation,get返回的class type的私有变量引用(偏移地址),映射到以class instantiation起始的地址上,获取实例变量的实际访问地址

拿到class instantiation的实际访问地址后,可以对变量进行随意读写

11位女生真实初夜经历,背后的心理与社会探讨
额头有痘痘怎么办