我可以在不使用朋友的情况下从课外访问私人会员吗?

Jas*_*ker 64 c++ encapsulation private-members

放弃

是的,我完全清楚我所询问的内容是完全愚蠢的,任何想要在生产代码中尝试这样的事情的人都应该被解雇和/或开枪.我主要是想看看是否可以做到.

现在已经不在了,有没有办法从类外部访问C++中的私有类成员?例如,有没有办法用指针偏移来做到这一点?

(天真和其他非生产准备技术欢迎)

更新

正如评论中所指出的,我问了这个问题,因为我想写一篇关于过度封装的博客文章(以及它如何影响TDD).我想看看是否有办法说"使用私有变量不是100%可靠的方法来强制封装,即使在C++中也是如此." 最后,我决定更多地关注如何解决问题,而不是为什么这是一个问题,所以我没有像我原先计划的那样突出显示这里提到的一些东西,但我还是留下了一个链接.

无论如何,如果有人对它的出现感兴趣,那么它就是: 测试驱动开发的敌人第一部分:封装(我建议在你决定我疯了之前阅读它).

dal*_*lle 69

如果类包含任何模板成员函数,您可以专门化该成员函数以满足您的需要.即使原始开发人员没有想到它.

safe.h

class safe
{
    int money;

public:
    safe()
     : money(1000000)
    {
    }

    template <typename T>
    void backdoor()
    {
        // Do some stuff.
    }
};
Run Code Online (Sandbox Code Playgroud)

main.cpp中:

#include <safe.h>
#include <iostream>

class key;

template <>
void safe::backdoor<key>()
{
    // My specialization.
    money -= 100000;
    std::cout << money << "\n";
}

int main()
{
    safe s;
    s.backdoor<key>();
    s.backdoor<key>();
}
Run Code Online (Sandbox Code Playgroud)

输出:

900000
800000
Run Code Online (Sandbox Code Playgroud)

  • 密钥有可能发生冲突.将它放在匿名命名空间中. (14认同)

Joh*_*itb 54

我在我的博客中添加了一个条目(见下文),展示了如何完成它.以下是如何将其用于以下类的示例

struct A {
private:
  int member;
};
Run Code Online (Sandbox Code Playgroud)

只需为它描述一个结构,然后实例化用于抢劫的实现类

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

template struct Rob<A_member, &A::member>;

int main() {
  A a;
  a.*get(A_member()) = 42; // write 42 to it
  std::cout << "proof: " << a.*get(A_member()) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

Rob类模板的定义是这样的需要,只有定义一次,不管有多少私有成员计划访问

template<typename Tag, typename Tag::type M>
struct Rob { 
  friend typename Tag::type get(Tag) {
    return M;
  }
};
Run Code Online (Sandbox Code Playgroud)

但是,这并未表明c ++的访问规则不可靠.语言规则旨在防止意外错误 - 如果您尝试抢夺对象的数据,则设计语言不会花费很长时间来阻止您.

  • 如果答案对您来说很棘手,请注意两个要点:1) `template struct Rob&lt;A_member, &amp;A::member&gt;;` 这是一个显式的模板类实例化。`&amp;A::member` 在这里工作 [因为](http://en.cppreference.com/w/cpp/language/class_template):“显式实例化定义忽略成员访问说明符:参数类型和返回类型可能是私有的” (2认同)
  • 2) `a.*get(A_member())` 调用非成员友元函数 `get(A_member)`,它返回 `&amp;A::member`。这是因为“[朋友注入](https://bytes.com/topic/c/answers/820592-template-friend-function-injection)”。尝试删除 get() 函数的“无用”参数,您应该得到“get 未在此范围内声明”(我希望看到对此行为的简明解释)。 (2认同)

Chr*_*isW 29

以下是偷偷摸摸,非法,依赖编译器,并且可能无法工作,具体取决于各种实现细节.

#define private public
#define class struct
Run Code Online (Sandbox Code Playgroud)

但它是你的OP的答案,你明确地邀请了一种技术,我引用它是"完全愚蠢的,并且任何希望在生产代码中尝试这样的事情的人都应该被解雇和/或开枪".


另一种技术是通过使用来自对象开头的硬编码/手动编码偏移来构造指针来访问私有成员数据.

  • 您还需要#define类struct,否则默认情况下私有可能会阻止您. (6认同)

Sma*_*acL 24

嗯,不知道这是否有效,但可能值得一试.创建另一个类,其布局与具有私有成员的对象相同,但私有更改为public.创建指向此类的指针变量.使用简单的强制转换将其指向具有私有成员的对象,并尝试调用私有函数.

期待火花,也许是崩溃;)

  • “具有相同的布局”。这是困难的部分。编译器在如何在非 POD 中布置成员方面有很大的自由度。但在实践中,它可能会奏效。 (2认同)

Rob*_*b K 12

class A 
{ 
   int a; 
}
class B
{
   public: 
   int b;
}

union 
{ 
    A a; 
    B b; 
};
Run Code Online (Sandbox Code Playgroud)

应该这样做.

ETA:它适用于这种琐碎的课程,但作为一般情况,它不会.

TC++ PL Section C.8.3:"具有构造函数,析构函数或复制操作的类不能是union成员的类型......因为编译器不知道要销毁哪个成员."

所以我们留下最好的选择是宣布class B匹配A的布局和黑客来看一个班级的私人.


Mar*_*ork 10

如果您可以获得指向类成员的指针,则无论访问说明符是什么(甚至是方法),都可以使用指针.

class X;
typedef void (X::*METHOD)(int);

class X
{
    private:
       void test(int) {}
    public:
       METHOD getMethod() { return &X::test;}
};

int main()
{
     X      x;
     METHOD m = x.getMethod();

     X     y;
     (y.*m)(5);
}
Run Code Online (Sandbox Code Playgroud)

当然,我最喜欢的小黑客是后门的朋友模板.

class Z
{
    public:
        template<typename X>
        void backDoor(X const& p);
    private:
        int x;
        int y;
};
Run Code Online (Sandbox Code Playgroud)

假设上面的创建者为他的正常用途定义了backDoor.但是您想要访问该对象并查看私有成员变量.即使将上面的类编译成静态库,您也可以为backDoor添加自己的模板特化,从而访问成员.

namespace
{
    // Make this inside an anonymous namespace so
    // that it does not clash with any real types.
    class Y{};
}
// Now do a template specialization for the method.
template<>
void Z::backDoor<Y>(Y const& p)
{
     // I now have access to the private members of Z
}

int main()
{
    Z  z;   // Your object Z

    // Use the Y object to carry the payload into the method.
    z.backDoor(Y());
}
Run Code Online (Sandbox Code Playgroud)


Jar*_*Par 8

绝对可以使用C++中的指针偏移来访问私有成员.让我们假设我有以下类型定义,我想访问.

class Bar {
  SomeOtherType _m1;
  int _m2;
};
Run Code Online (Sandbox Code Playgroud)

假设Bar中没有虚拟方法,那么简单的情况就是_m1.C++中的成员存储为对象的内存位置的偏移量.第一个对象位于偏移0处,第二个对象位于sizeof(第一个成员)的偏移处,等等...

所以这是一种访问_m1的方法.

SomeOtherType& GetM1(Bar* pBar) {
  return*(reinterpret_cast<SomeOtherType*>(pBar)); 
}
Run Code Online (Sandbox Code Playgroud)

现在_m2有点困难了.我们需要从原始字节移动原始指针sizeof(SomeOtherType)字节.转换为char是为了确保我以字节偏移量递增

int& GetM2(Bar* pBar) {
  char* p = reinterpret_cast<char*>(pBar);
  p += sizeof(SomeOtherType);
  return *(reinterpret_cast<int*>(p));
}
Run Code Online (Sandbox Code Playgroud)


iam*_*ind 7

这个答案是基于@Johannes的答案/博客所展示的确切概念,因为这似乎是唯一的“合法”方式。我已将该示例代码转换为一个方便的实用程序。它很容易与 C++03 兼容(通过实现std::remove_reference和替换nullptr)。

图书馆

#define CONCATE_(X, Y) X##Y
#define CONCATE(X, Y) CONCATE_(X, Y)

#define ALLOW_ACCESS(CLASS, MEMBER, ...) \
  template<typename Only, __VA_ARGS__ CLASS::*Member> \
  struct CONCATE(MEMBER, __LINE__) { friend __VA_ARGS__ CLASS::*Access(Only*) { return Member; } }; \
  template<typename> struct Only_##MEMBER; \
  template<> struct Only_##MEMBER<CLASS> { friend __VA_ARGS__ CLASS::*Access(Only_##MEMBER<CLASS>*); }; \
  template struct CONCATE(MEMBER, __LINE__)<Only_##MEMBER<CLASS>, &CLASS::MEMBER>

#define ACCESS(OBJECT, MEMBER) \     
 (OBJECT).*Access((Only_##MEMBER<std::remove_reference<decltype(OBJECT)>::type>*)nullptr)
Run Code Online (Sandbox Code Playgroud)

应用程序编程接口

ALLOW_ACCESS(<class>, <member>, <type>);
Run Code Online (Sandbox Code Playgroud)

用法

ACCESS(<object>, <member>) = <value>;   // 1
auto& ref = ACCESS(<object>, <member>); // 2
Run Code Online (Sandbox Code Playgroud)

演示

struct X {
  int get_member () const { return member; };
private:
  int member = 0;
};

ALLOW_ACCESS(X, member, int);

int main() {
  X x;
  ACCESS(x, member) = 42;
  std::cout << "proof: " << x.get_member() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)