衆所周知,面向對象的語言都會有類似於private,protected,public之類的權限控制。其實如前一篇blog所講(http://blog.csdn.net/cracker_zhou/article/details/70956249) :在C++中,權限只是爲了在代碼的運行期阻止獲得變量或者某個函數的地址而達到不能調用的作用。言外之意就是只要能搞到內存地址,什麼都好說。下面提供幾種hack私有成員變量地址的方法。
1. 計算成員變量內存偏移
我相信對C++內存分配懂一點的小夥伴都知道這種方法。
class B
{
private:
int b;
std::string x;
char* ch;
public:
int getint(){ return b; }
std::string getstr(){ return x; }
char* getch(){ return ch; }
};
int main()
{
B b;
*(int*)&b = 20;
std::string* str = (std::string*)((long)&b + sizeof(int));
str->append("zhou");
char** ch = (char**)((long)str+sizeof(std::string));
*ch = new char('a');
std::cout << "int:" << b.getint() << std::endl // int:20
<< "str:" << b.getstr().c_str() << std::endl // str:"zhou"
<< "char*:" << *b.getch() << std::endl; // char*:'a'
delete *ch;
return 0;
}
2. 類的等效替換
上面通過計算成員變量的偏移確實可以hack內存地址,但是弊端也很明顯成員變量越多,後面就越需要計算偏移,而且二重指針也很容易搞出事情。那麼怎麼辦呢?我們只需要自定義一個擁有一樣內存分佈的數據結構即可。
class B
{
private:
int b;
std::string x;
char* ch;
public:
int getint(){ return b; }
std::string getstr(){ return x; }
char* getch(){ return ch; }
};
// 在此模擬一個與class B擁有相同內存分佈的struct,但權限都是開放的
typedef struct
{
public:
int b;
std::string x;
char* ch;
}Proof;
int main()
{
B b;
Proof* proof = (Proof*)&b;
proof->b = 10;
proof->x.append("zhou");
proof->ch = new char('a');
std::cout << "int:" << b.getint() << std::endl // int:10
<< "str:" << b.getstr().c_str() << std::endl // str:"zhou"
<< "char*:" << *b.getch() << std::endl; // char*:'a'
delete proof->ch;
return 0;
}
3. 元編程(meta programming)
之前一直以爲元編程只有python,js這種動態語言中有,但是沒想到C++的元編程也很強大。概念是一樣的,元編程(C++操作template,python操作type)的存在都是爲了動態生成類。但是我發現一個問題:C++的模板類中傳遞參數時並不會校驗權限問題。(比如上面中類B,當我們在運行時使用&B::b獲得偏移時會直接報權限錯誤,但是在模板中傳參並不會這樣!)
// 這是一個簡單的類模板傳值的例子
template<typename T,typename T V>
typename T f(T)
{
return V;
}
int main()
{
std::cout << f<int,10>(int()); // 10
return 0;
}
那麼如何把類成員地址傳出去呢?原理是相同的。
struct A {
private:
int a;
};
// tag used to access A::a
struct A_f {
using type = int A::*;
};
template<typename Tag, typename Tag::type M>
struct Rob {
friend typename Tag::type get(Tag) {
return M;
}
};
template struct Rob<A_f, &A::a>;
int main()
{
A a;
a.*get(A_f()) = 20; // 傳入A_f()純屬爲了把類型傳入,滿足template的語法要求
std::cout << a.*get(A_f()); // 得到地址後可以進行get和set操作
return 0;
}
第三種方案實在強大,版權歸外國某大神所有( http://stackoverflow.com/users/34509/johannes-schaub-litb ),它在stackoverflow原回答也貼出來(http://stackoverflow.com/questions/424104/can-i-access-private-members-from-outside-the-class-without-using-friends#3173080)