转自:http://blog.csdn.net/jiange_zh/article/details/52191144
假设有以下类:
m_nPrivate
class X
{
private:
int m_nPrivate;
public:
X()
: m_nPrivate(1)
{}
template<typename T>
void Func(const T &t)
{}
const int GetValue()
{
return m_nPrivate;
}
};
方法1:使用指针
void *p = &x; // 获取类的起始地址,其实也就是m_nPrivate数据成员的地址
int *n = (int *)p;
int tmp = 2;
*n = tmp; // 改写其值
cout << x.GetValue() << endl; // 输出为2
这种方法的缺点:计算偏移量是个麻烦的事情,涉及到内存对齐、编译器版本等,可移植性低,而且这种方法只能访问成员变量,却不能达到“使用private内置类型”的目的。
方法2:使用宏
在类X的定义前,加一句:
#define private public
该方法可以欺骗编译器,让它把“private”当作“public”。
然而它有两个违背标准的行为:
1)#define 保留字是非法的
2)违反了唯一定义规则(ODR,One Definition Rule),然而类的底层内存布局没改变,故可行
方法3:使用指针类型转换——偷天换日
m_nNotPrivate
// 同X的内存布局,将变量or类型定义改为public
class Y
{
public:
int m_nNotPrivate;
};
void Func(X* xPtr)
{
(reinterpret_cast<Y*>(xPtr))->m_nNotPrivate = 2;
}
首先我们将X类型的指针转换为Y类型的指针,在编译器看来,我们访问的是Y类型的public成员m_nNotPrivate,因此编译通过,然而事实上该指针是X类型的,由于Y跟X的内存布局是完全一样,因此访问Y的m_nNotPrivate成员实际上也就是在访问X的m_nPrivate成员。
类似的方法就是,在Y中增加一个非虚成员函数,该函数用来返回m_nPrivate的地址。
方法4:添加友元声明
类似以上方法,不过是通过添加友元声明来访问的~
方法5:利用模版合法钻空子
如果X中存在一个成员模版,那么可以这样子:
namespace
{
struct Y{};
}
template<>
void X::Func(const Y&) //特化
{
m_nPrivate = 2;
}
void Test()
{
X x;
cout << x.GetValue() << endl;
x.Func(Y());
cout << x.GetValue() << endl;
}
这种方法利用了X具有一个成员模板的事实,通过特化函数模版,来打入敌人内部。代码完全符合标准,标准也确保这种行为会按照编码者的意图行事。boost和loki中大量运用此手法。