如何突破类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