訪問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的實際訪問地址后,可以對變量進行隨意讀寫

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM