如何突破類private的訪問控制限制
類的訪問控制權限如下:
訪問 | public | protected | private |
---|---|---|---|
同一個類 | yes | yes | yes |
派生類 | yes | yes | no |
外部及外部類類 | yes | no | no |
接下來主要說一下外部函數及外部類幾種突破private訪問權限的方法。
調用公共成員函數
#include <iostream>
using namespace std;
class X {
private:
int a;
int b;
public:
X(): a(3), b(4){}
int geta(){
return a;
}
void seta(int x){
a=x;
}
int getb(){
return b;
}
void setb(int x){
b=x;
}
friend int setgetbx(X &,int x);
};
int main()
{
X s=X();
cout<<"init a="<<s.geta()<<endl;
cout<<"init b="<<s.getb()<<endl;
s.seta(10);
s.setb(10);
cout<<"reset a="<<s.geta()<<endl;
cout<<"reset b="<<s.getb()<<endl;
return 0;
}
運行結果:
友元函數
友元函數一般放在類的開始或者結束位置,在類內聲明+friend關鍵字,在類外定義
#include <iostream>
using namespace std;
class X {
private:
int a;
int b;
public:
X(): a(3), b(4){}
int geta(){
return a;
}
int getb(){
return b;
}
friend int setgetbx(X & x,int temp); // 類內聲明友元函數
};
int setgetbx(X & x,int temp){ // 類外定義友元函數
x.b+=temp;
return x.b;
}
int main()
{
X s=X();
cout<<"init a="<<s.geta()<<endl;
cout<<"init b="<<s.getb()<<endl;
int newb=setgetbx(s,5); //調用友元函數
cout<<"reset b="<<newb<<endl;
cout<<"reset b="<<s.getb()<<endl;
return 0;
}
運行結果:
使用 指針與引用訪問
#include <iostream>
using namespace std;
class X {
private:
int a;
int b;
public:
X(): a(3), b(4){}
int geta(){
return a;
}
int getb(){
return b;
}
};
int main()
{
X s=X();
cout<<"init a="<<s.geta()<<endl;
cout<<"init b="<<s.getb()<<endl;
int *ptr=(int *)(&s); // 類指針
cout<<"init a="<<*ptr<<endl;
cout<<"init b="<<*(ptr+1)<<endl;
*ptr=5; //指向a
*(ptr+1)=5; // 指向b
cout<<"pointer reset a="<<s.geta()<<endl;
cout<<"pointer reset b="<<s.getb()<<endl;
return 0;
}
運行結果:
這裏需要注意,如果有虛函數的話,指針需要加+ *(ptr+1) *(ptr+2) 因爲虛函數表是類內第一個位置。
指針的類型裝換
#include <iostream>
using namespace std;
class X {
private:
int a;
int b;
public:
X(): a(3), b(4){}
int geta(){
return a;
}
int getb(){
return b;
}
};
class Y
{
public:
int a; //與 X裏面a對應
int b; //與 X裏面b對應
};
void Func(X* xPtr)
{
// reinterpret_cast 用於進行各種不同類型的指針之間、
// 不同類型的引用之間以及指針和能容納指針的整數類型之間的轉換。
(reinterpret_cast<Y*>(xPtr))->b = 2;
}
int main()
{
X s=X();
cout<<"init a="<<s.geta()<<endl;
cout<<"init b="<<s.getb()<<endl;
Func(&s);
Func(&s);
//cout<<"reinterpret_cast reset a="<<s.geta()<<endl;
cout<<"reinterpret_cast reset b="<<s.getb()<<endl;
return 0;
}
下面結果爲類X與Y內存對齊的情況,即X與Y 裏面的成員變量x.a x.b的位置一一對應:
如果不對齊的話看下面一個實驗
#include <iostream>
using namespace std;
class X {
private:
int a;
int b;
public:
X(): a(3), b(4){}
int geta(){
return a;
}
int getb(){
return b;
}
};
class Y
{
public:
int b;
};
void Func(X* xPtr)
{
// reinterpret_cast 用於進行各種不同類型的指針之間、
// 不同類型的引用之間以及指針和能容納指針的整數類型之間的轉換。
(reinterpret_cast<Y*>(xPtr))->b = 2;
}
int main()
{
X s=X();
cout<<"init a="<<s.geta()<<endl;
cout<<"init b="<<s.getb()<<endl;
Func(&s); //attention
cout<<"reinterpret_cast reset a="<<s.geta()<<endl;
cout<<"reinterpret_cast reset b="<<s.getb()<<endl;
return 0;
}
此時修改Y的b 但是確實修改X裏面的a,這是因爲 Y的b 與X的啊 在類內具有相同的內存位置,歸根究低還是對指針與實際內存的操作:
利用模版合法
#include <iostream>
using namespace std;
class X {
private:
int a;
int b;
public:
X(): a(3), b(4){}
template<typename T> //在X類內定義成員模板函數
void Func(const T &t){}
int geta(){
return a;
}
int getb(){
return b;
}
};
class Y {}; //外部Y類
template<>
void X::Func(const Y&) //特化 //attention
{
a=2;
b=8;
}
int main()
{
X s=X();
cout<<"init a="<<s.geta()<<endl;
cout<<"init b="<<s.getb()<<endl;
s.Func(Y()); //attention
cout<<"template reset a="<<s.geta()<<endl;
cout<<"template reset b="<<s.getb()<<endl;
return 0;
}
運行結果:
這種方法利用了X具有一個成員模板的事實,通過特化函數模版,來打入敵人內部。代碼完全符合標準,標準也確保這種行爲會按照編碼者的意圖行事。boost和loki中大量運用此手法。
參考:http://www.luyixian.cn/news_show_37214.aspx