zoukankan      html  css  js  c++  java
  • 访问c++类的私有成员

    访问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 <typename PtrType, PtrType PtrValue, typename TagType>
     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 <typename PtrType, PtrType PtrValue>
    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的实际访问地址后,可以对变量进行随意读写

  • 相关阅读:
    面试中你能做到随机应变吗? 沧海
    QQ只是一场意外 沧海
    面 试 中 要 慎 言 沧海
    你会应对这些面试题吗? 沧海
    面 试 小 技 巧 沧海
    面试抓住最初三分钟至关重要 沧海
    面试的十二种高级错误 沧海
    几种有难度的面试 沧海
    面试技巧: 轻松过关10种方法 沧海
    面 试 细 节 一 点 通 沧海
  • 原文地址:https://www.cnblogs.com/hgwang/p/15567133.html
Copyright © 2011-2022 走看看