学习笔记:私有成员的访问

问题:

如何访问类的私有成员? 前提是不留后门

</pre><pre name="code" class="cpp">class Test{
    void f(){}
    int m_;
};

解答1)

用友元。友元是最明显的后门,这个答案不算数。

解答2)

用成员指针。

class Test;
typedef void (Test::*PF)();
typedef int Test::* PM;

class Test
{
private:
    void f(){}
    int m_;
public:
    PF getpf() { return &Test::f;}
    PM getpm() { return &Test::m_;}
};

int main()
{
     Test  x;
     PF pf = x.getpf();
     PM pm = x.getpm();

     (x.*pf)();
     x.*pm = 10;

}
通过成员函数指针,可以不受私有的限制,任意调用。但是既然能改动源代码,为什么不直接开发public的访问函数呢?而且, getpf()等类似东西是一种类似firend的后门。

这个解答也不可取。

解答3)

用模板函数

class Test
{
    int m_;
public:
    template <int Dummy>  void backdoor(){}
};

template <>
void Test::backdoor<0>()
{
    m_=0;
}

int main()
{
    Test t;
    t.backdoor<0>();
}
虽然很巧妙,但仍旧需要在Test类上开后门,这种“侵入式”修改不算黑客方式,更像哗众取宠。


解答4)

更加黑客的方式是,知道Test类的布局,从偏移地址计算成员的地址。

//#define OFFSET(Test, m_) ???

int main()
{
    Test t;

    char* start = reinterpret_cast<char*>(&t);
    char* pm    = start + 0;
    int*  pmi   =  reinterpret_cast<int*>(pm);
    *pmi = 123; 

}

我们知道Test中的成员m_的偏移地址是0,就可以计算t.m_的地址了。但是,这个0是魔术数字,当Test的布局发生变化,m_的偏移地址很可能就不是0了。

如果想更自动化的方式,例如 OFFSET(Test, m_) 自动计算偏移地址,就能避免使用魔术数字问题。

很可惜,当你发现一个 #define offsetof(s,m) (size_t)&(((s *)0)->m) 可能能用上时,却又发现offsetof(Test,m_)编译不过去,因为Test::m_是私有的。


解答5)

我拷贝一份相似代码, 只是把私有变成公有

<pre name="code" class="cpp">class Test{
private:
    int m_;
};

class Test2{
public:
    int m_;
};

int main()
{
    Test t;

    Test2& t2 = reinterpret_cast<Test2&>(t);
    t2.m_=10;
}

 

此方案的缺点是把魔术字取消了,却引入一个魔术类。当Test发生变化后,Test2不会自动跟着变,因此会产生bug,甚至段错误。


解答6)

点击打开链接

template<typename Tag>
struct result {
  /* export it ... */
  typedef typename Tag::type type;
  static type ptr;
};

template<typename Tag>
typename result<Tag>::type result<Tag>::ptr;

template<typename Tag, typename Tag::type p>
struct rob : result<Tag> {
  /* fill it ... */
  struct filler {
    filler() { result<Tag>::ptr = p; }
  };
  static filler filler_obj;
};

template<typename Tag, typename Tag::type p>
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;
struct A {
private:
  void f() {
    std::cout << "proof!" << std::endl;
  }
};

struct Af { typedef void(A::*type)(); };
template class rob<Af, &A::f>;
int main() {
  A a;
  (a.*result<Af>::ptr)();
}

一种非侵入式,获取成员指针的方法。可能是利用编译器漏洞。

理论上说,不能随便就偷窃到私有成员的指针。但是这个模板方案就做到了。不排除将来编译器堵死这个漏洞的可能性。


总结:

研究私有成员的访问方法,实际上是考察意外的修改私有成员的可能性。通过穷思苦想,得到的结论是,私有成员的访问都不是意外的,甚至是故意的(如方案6)

通过暴力访问,总能修改任意内存中的数据(包括私有数据),不能考虑那些歪门邪道。我们只考虑在正常使用C++语言后,编译器能否帮助程序员检查出逻辑错误。

通过特殊手段访问私有数据,在编写单元测试程序时特别有利。单元测试要访问和调用私有函数和私有数据,如果是非侵入式的手段,将大大有利于代码的清晰性和可维护性




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值