私人成员黑客定义的行为?

时间:2010-05-14 13:18:35

标签: c++ casting private-members

我有以下课程:

class BritneySpears
{
  public:

    int getValue() { return m_value; };

  private:

    int m_value;
};

哪个是外部库(我无法更改)。我显然无法改变m_value的值,只读它。即使从BritneySpears派生也行不通。

如果我定义以下类,该怎么办:

class AshtonKutcher
{
  public:

    int getValue() { return m_value; };

  public:

    int m_value;
};

然后做:

BritneySpears b;

// Here comes the ugly hack
AshtonKutcher* a = reinterpret_cast<AshtonKutcher*>(&b);
a->m_value = 17;

// Print out the value
std::cout << b.getValue() << std::endl;

我知道这是糟糕练习。但出于好奇:这是否可以保证有效?是否定义了行为?

奖金问题:你有没有必要使用这样丑陋的黑客?

编辑:只是为了吓唬更少的人:我不打算在真正的代码中实际执行此操作。我只是想知道;)

6 个答案:

答案 0 :(得分:21)

这是未定义的行为。每个访问限定符部分中的成员都保证按其出现的顺序排列,但是在acccess限定符之间没有这样的保证。例如,如果编译器选择将所有私有成员放在所有公共成员之前,则上述两个类将具有不同的布局。

编辑:重新回答这个旧答案,我意识到我错过了一个相当明显的观点:结构定义每个都只有一个数据成员。成员函数的顺序无关紧要,因为它们不会影响类的布局。你可能会发现两个数据成员都保证在同一个地方,但我不太清楚这个标准是否足以说明。

但是!您无法取消引用不相关类型之间reinterpret_cast的结果。它仍然是UB。至少,这是我对http://en.cppreference.com/w/cpp/language/reinterpret_cast的阅读,这确实是一个粗略的阅读。

答案 1 :(得分:9)

这是未定义的行为,原因Marcelo指出。但有时你需要在集成无法修改的外部代码时采用这些方法。一种更简单的方法(以及同样未定义的行为)是:

#define private public
#include "BritneySpears.h"

答案 2 :(得分:4)

您可能无法修改BritneySpears的库,但您应该能够修改.h头文件。如果是,您可以AshtonKutcher成为BritneySpears的朋友:

class BritneySpears 
{
    friend class AshtonKutcher;
  public: 

    int getValue() { return m_value; }; 

  private: 

    int m_value; 
}; 

class AshtonKutcher 
{ 
  public: 

    int getValue(const BritneySpears & ref) { return ref.m_value; }; 
}; 

我真的不能宽恕这个伎俩,我不认为我自己曾经尝试过,但它应该是法律定义良好的C ++。

答案 3 :(得分:2)

@Marcelo说得对:在不同的访问级别上,成员的顺序是不确定的。

但请考虑以下代码;在这里,AshtonKutcherBritneySpears具有完全相同的布局:

class AshtonKutcher
{
  public:
    int getValue() { return m_value; };
    friend void setValue(AshtonKutcher&, int);

  private:
    int m_value;
};

void setValue(AshtonKutcher& ac, int value) {
    ac.m_Value = value;
}

我相信这实际上可能是有效的C ++。

答案 4 :(得分:2)

您的代码存在问题,答案下划线。问题来自订购价值。

然而你几乎就在那里:

class AshtonKutcher
{
public:

  int getValue() const { return m_value; }
  int& getValue() { return m_value; }

private:
  int m_value;
};

现在,您具有完全相同的布局,因为您具有相同的属性,以相同的顺序声明,并具有相同的访问权限......并且两个对象都没有虚拟表。

因此,诀窍不是改变访问级别,而是添加方法:)

当然,除非我错过了什么。

我确切认为这是维护噩梦吗?

答案 5 :(得分:1)

通常应避免使用reinterpret_cast,并且无法保证portable results

另外,为什么要更改私人会员?您可以将原始类包装在一个新类中(更喜欢组合而不是继承)并根据需要处理getValue方法。