允许访问私人会员

时间:2013-02-27 11:07:57

标签: c++ class templates inheritance private

这个问题在某种程度上延续了this one我发布的内容。

我尝试做什么:我的观点是允许在派生类A中访问基类B的私有成员,具有以下限制:

  • 我想要访问的是一个结构 - 实际上是std::map<> - 而不是方法;
  • 无法修改基类;
  • 基类A没有模板化方法我可能会作为后门替代方法重载 - 我不会添加这样的方法,因为它将反对第二个约束。

作为一种可能的解决方案,我已经指出了litb的解决方案(post / blog),但是,对于我的生活,我还没有能够了解这些帖子的内容,因此,我无法找到解决问题的方法。

我正在尝试做什么:以下代码来自litb的解决方案,介绍了如何从类/结构中访问私有成员的方法,它恰好覆盖了我提到的限制。

所以,我试图重新排列这一个代码:

template<typename Tag, typename Tag::type M>
struct Rob { 
  friend typename Tag::type get(Tag) {
    return M;
  }
};

// use
struct A {
  A(int a):a(a) { }
private:
  int a;
};

// tag used to access A::a
struct A_f { 
  typedef int A::*type;
  friend type get(A_f);
};

template struct Rob<A_f, &A::a>;

int main() {
  A a(42);
  std::cout << "proof: " << a.*get(A_f()) << std::endl;
}

对于允许我执行以下操作的内容 - 请注意我即将继承该类,因为std::map<>中的条目是在派生类{{1}初始化之后添加的},即B不仅仅是具有默认值的类std::map<>的静态成员,因此我需要从A的特定实例访问它:< / p>

B

我想要的答案:我真的让上述代码正常工作 - 经过适当的修改 - ,但我要非常内容,并解释第一个代码块中的内容 - 来自litb的解决方案。

4 个答案:

答案 0 :(得分:6)

遗憾的是,博客文章及其代码有点不清楚。概念很简单:显式模板实例化获得任意类的免费后台传递,因为

  • 库类的显式实例化可以是客户端类的实现细节,
  • 显式实例化只能在命名空间范围内声明。

分发此后台传递的自然方式是指向成员的指针。如果您有指向给定类成员的指针,则无论访问限制如何,都可以在该类的任何对象中访问它。幸运的是,即使在C ++ 03中,指向成员的指针也可以是编译时常量。

因此,我们需要一个在显式实例化时生成指向成员的指针的类。

显式实例化只是定义类的一种方式。如何只生成一个类的东西?有两种选择:

  • 定义friend函数,该函数不是该类的成员。这就是litb所做的。
  • 定义静态数据成员,该成员在启动时初始化。这是我的风格。

我首先介绍我的风格,然后讨论它的缺点,然后修改它以匹配litb的机制。最终结果仍然比博客中的代码更简单。

简易版。

该类有三个模板参数:受限成员的类型,实际名称以及对接收指向它的指针的全局变量的引用。该类调度要初始化的静态对象,其构造函数初始化全局。

template< typename type, type value, type & receiver >
class access_bypass {
    static struct mover {
        mover()
            { receiver = value; }
    } m;
};

template< typename type, type value, type & receiver >
typename access_bypass< type, value, receiver >::mover
    access_bypass< type, value, receiver >::m;

用法:

type_of_private_member target::* backstage_pass;
template class access_bypass <
    type_of_private_member target::*,
    & target::member_name,
    backstage_pass
>;

target t;
t.* backstage_pass = blah;

See it work.

不幸的是,在程序进入main之前,你不能依赖于其他源文件中的全局对象构造函数可用的结果,因为没有标准的方法告诉编译器在哪个命令初始化文件但是全局变量按它们声明的顺序初始化,所以只要静态对象构造函数不对其他文件进行函数调用就可以把你的旁路放在顶部,你就可以了。

强大的版本。

这通过添加标签结构和friend函数借用litb代码中的元素,但这是一个小修改,我认为它仍然非常清楚,并不比上面更糟糕。

template< typename type, type value, typename tag >
class access_bypass {
    friend type get( tag )
        { return value; }
};

用法:

struct backstage_pass {}; // now this is a dummy structure, not an object!
type_of_private_member target::* get( backstage_pass ); // declare fn to call

// Explicitly instantiating the class generates the fn declared above.
template class access_bypass <
    type_of_private_member target::*,
    & target::member_name,
    backstage_pass
>;

target t;
t.* get( backstage_pass() ) = blah;

See it work.

这个强大的版本和litb的博客文章之间的主要区别在于我已将所有参数收集到一个地方并使标记结构为空。它只是一个相同机制的更清晰的接口。但是你必须声明get函数,博客代码会自动执行该函数。

答案 1 :(得分:4)

好的,所以你问过如何让那个奇怪的“Rob”代码与你的用例一起工作,所以就是这样。

// the magic robber
template<typename Tag, typename Tag::type M>
struct Rob {
    friend typename Tag::type get(Tag) {
        return M;
    }
};

// the class you can't modify
class A {
private:
    std::map<int, int> A_map;
};

struct A_f {
    typedef std::map<int, int> A::*type;
    friend type get(A_f);
};

template struct Rob<A_f, &A::A_map>;

class B : private A {
public:
    inline void uncover() {
        std::map<int, int>::iterator it = (this->*get(A_f())).begin();
    }
};

现在,我个人认为这里的治疗可能比疾病更糟,尽管我通常是你会看到的最后一个声称滥用C ++的人。你可以自己决定,所以我把它作为一个单独的答案发布,使用预处理器以老派的方式来做。

编辑:

如何运作

在这里,我将复制上面的代码,但简化的类型和更多的代码,以及大量的评论。请注意,在我进行这项练习之前,我并没有很好地理解代码,我现在还不完全理解它,我当然不会记得它明天是如何工作的。警告维护者。

以下是我们不允许更改的代码,私有成员:

// we can use any type of value, but int is simple
typedef int value_type;

// this structure holds value securely.  we think.
struct FortKnox {
    FortKnox() : value(0) {}
private:
    value_type value;
};

现在为了抢劫:

// define a type which is a pointer to the member we want to steal
typedef value_type FortKnox::* stolen_mem_ptr;

// this guy is sort of dumb, but he knows a backdoor in the standard
template<typename AccompliceTag, stolen_mem_ptr MemPtr>
struct Robber {
    friend stolen_mem_ptr steal(AccompliceTag) {
        return MemPtr; // the only reason we exist: to expose the goods
    }
};

// this guy doesn't know how to get the value, but he has a friend who does
struct Accomplice {
    friend stolen_mem_ptr steal(Accomplice);
};

// explicit instantiation ignores private access specifier on value
// we cannot create an object of this type, because the value is inaccessible
// but we can, thanks to the C++ standard, use this value in this specific way
template struct Robber<Accomplice, &FortKnox::value>;

// here we create something based on secure principles, but which is not secure
class FortKnoxFacade : private FortKnox {
public:
    value_type get_value() const {
        // prepare to steal the value
        // this theft can only be perpetrated by using an accomplice
        stolen_mem_ptr accessor = steal(Accomplice()); // it's over now
        // dereference the pointer-to-member, using this as the target
        return this->*accessor;
    }
};

int main() {
    FortKnoxFacade fort;
    return fort.get_value();
}

答案 2 :(得分:3)

更残酷的事情怎么样?

// MY CODE -- module "b.h"
# define private protected
# include <a.h>
# undef private
class B : private A {
    // now you can access "private" members and methods in A

答案 3 :(得分:1)

我对这个习语的最佳打包版本如下:

template<class Tag,typename Tag::type MemberPtr>
struct access_cast{
 friend typename Tag::type get(Tag){return MemberPtr;};
};

template<class Tag,class MemberPtr>
struct access_tag{
 typedef MemberPtr type;
 friend type get(Tag);
};

class A {
public:
 auto x() const {return x_;};
private: 
 int x_ = 9;
};

#include <iostream>

struct AMemTag: access_tag<AMemTag,int A::*>{}; //declare tag
template struct access_cast<AMemTag,&A::x_>; //define friend get function

int main() {
 A a;
 std::cout<<a.x()<<"\n";
 a.*get(AMemTag()) = 4; //dereference returned member pointer and modify value
 std::cout<<a.x()<<"\n";
}

See it work.