【问题标题】:Class Private members modified on creating a structure (C++)创建结构时修改的类私有成员 (C++)
【发布时间】:2011-04-18 17:37:39
【问题描述】:

我只是在浏览一些 C++ 代码。 我在哪里遇到了reinterpret_cast 运算符的概念。

编辑 1:

我知道不建议访问类的私有成员。 但在某些情况下,我们应该继续访问它们。 我刚刚提出这个问题是为了弄清楚我的概念。

在我提到的示例中,通过简单地创建具有相同变量的结构来访问 Class 的私有成员,然后通过实现进行修改 reinterpret_cast运营商。

我已经了解reinterpret_cast 运算符的用法,因为我知道它的作用是什么,但我不明白如何可以使用结构来修改私有类成员的值。

以下是我引用的源代码:

类:

class Student
{
public:
    explicit Student(float percent) // Cannot be used for conversion
    {
        static int nid;

        id = ++nid;
        score = percent;
    }

    int Id() const
    {
        return id;
    }

    float GetScore() const
    {
        return score;
    }

    void SetScore(float value)
    {
        score = value;
    }

    virtual ~Student(){}

private:
    int id;
    float score;
};

用于访问和修改私有类成员的结构:

struct _Student
    {
        void* vptr;
        int id;
        float score;
    };

    _Student* bs3 = reinterpret_cast<_Student*>(bs2);
    bs3->id = 5;

谢谢。如果我错了/我无法以适当的方式提出我的问题,请纠正我。

【问题讨论】:

  • 嗯,也许您可​​以实现一个 friend 类,该类具有将带有私有成员的对象转换为暴露这些私有成员的结构或数据类型的功能。这将使您的代码更干净,更少邪恶:)
  • 是的。这个问题只是为了弄清楚我的基本原理。我知道它不推荐。检查编辑 1。
  • 我确实看到了您的免责声明,但这简直是邪恶的(除非您可以通过解释为什么不能以其他方式解决它来证明相反的情况)。

标签: c++ oop class reinterpret-cast


【解决方案1】:

我认为您或许应该稍微改变一下您的问题的上下文。 如果你意识到你需要在你的类中访问一个私有变量,那么 您正面临一个需要解决的设计问题,而不是 使用不安全的类型转换进行黑客攻击。哪怕只是 假设性的,为了在这里询问 reinterpret_cast。

至于一个有意义的 reinterpret_cast 用例,我会在哈希函数中说:

unsigned short Hash( void *p ) {

unsigned int val = reinterpret_cast<unsigned int>( p );
return ( unsigned short )( val ^ (val >> 16));

}

一些有用信息的链接:

When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?

http://advancedcppwithexamples.blogspot.com/2010/02/reinterpretcast-in-c.html

http://www.linuxtopia.org/online_books/programming_books/thinking_in_c++/Chapter03_054.html

【讨论】:

    【解决方案2】:

    你所拥有的是一个关于封装的可怕黑客攻击。如果您真的想要访问私有变量,那么您应该使用“friend”关键字。 reinterpret_cast 起作用的原因是因为您将 Student 类的字节解释为 struct _Student - 它的变量默认声明为 public。访问私有数据的方式有很多种,这是我能想到的另一种方式:

    int* bs3 = reinterpret_cast<int*>(bs2);
    ++bs3;
    *bs3 = 5;
    

    我的建议是不要这样做。

    【讨论】:

      【解决方案3】:

      $5.2.10/2 - “一个表达式 积分、枚举、指针或 指向成员类型的指针可以是 显式转换为自己的类型; 这样的转换会产生其操作数的值。"

      这意味着指针'bs2'和'bs3'指向同一个位置

      $9.2/16 - “两个标准布局结构 (第 9 条)类型是布局兼容的 如果他们有相同数量的 非静态数据成员和 对应的非静态数据成员 (按申报顺序)有 布局兼容类型 (3.9)。”

      这意味着你的类和结构是布局兼容的。

      $9/6-

      标准布局类是一个类 那个:

      ——没有非静态数据成员 类型非标准布局类(或 此类类型的数组)或引用,

      ——没有虚函数 (10.3) 并且没有 虚拟基类 (10.1),

      ——具有相同的访问控制(第 11 条) 所有非静态数据成员,

      ——没有非标准布局的基类,

      ——要么没有非静态数据成员 在最派生的类中,最多 一个具有非静态数据的基类 成员,或者没有基类 非静态数据成员,以及

      ——没有与 第一个非静态数据成员。108

      由于您的类具有虚拟析构函数,因此您的类和结构不是标准布局类。

      但是,您添加了一个“void *”数据成员来处理“vptr”(从而可能根据您的特定编译器实现模仿布局兼容性)

      在这种情况下,reinterpret_cast 用于将类指针 (bs2) 解释为结构指针 (bs3)。默认情况下,结构成员是公共的。由于reinterpret cast的返回值指向类成员所在的同一个内存(参考上面的引用),所以可以修改struct成员(与原来的类成员相同)。

      这是作弊。这是非常不鼓励的。!这很可能会导致未定义的行为

      【讨论】:

      • 结构和类不是布局兼容的:结构有一个 void*,类没有。不,vtable 指针不算数,因为 C++ 标准没有强制要求它。底线:它可能适用于您当前的编译器,但 C++ 标准不保证它。
      • @Sjoerd:是的 :),我一提交修改后的更新,就看到了你们的 cmets。
      【解决方案4】:

      但在某些情况下,我们应该继续访问它们。

      请问,这些情况是什么?

      除了设计错误,我看不到任何错误。访问私人成员是不行的。如果您需要访问权限,请通过合法方式提供,即让成员更易于访问或使用friend 修饰符以受控方式访问它们。

      违反 C++ 类型检查系统就等于游戏结束:你欺骗了编译器,别指望它会和你一起工作。对于这种未定义的行为(即不仅依赖于平台,而且出于充分的理由而被禁止),您只是在以 非常难以跟踪错误的形式招致麻烦。

      tl;dr:不要。永远。

      警告:有一个例外:您有一个无法访问/修改其源代码的库,并且您无法影响其界面设计。 另外你可以确定(如何?)它永远不会改变。在这种情况下,唯一的解决方案可能是用这些技巧来破解库。

      【讨论】:

        【解决方案5】:

        但在某些情况下,我们应该继续访问它们。

        如果您必须在任何情况下访问它们,请更改其访问规范。

        或者更好的是,创建一个接受令牌(或一些特殊权限 - 以验证调用者)的公共方法,并返回请求的值。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-22
          • 2012-12-12
          • 2012-12-12
          • 1970-01-01
          • 1970-01-01
          • 2012-08-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多