私人会员黑客行为是定义行为吗?
我有以下课程:
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;
我知道这是不好的做法。但只是出于好奇:这保证有效吗?它是定义的行为吗?
额外问题:你曾经不得不使用如此丑陋的黑客吗?
编辑:只是为了吓唬更少的人:我不打算在真实代码中实际执行此操作。我只是想知道;)
I have the following class:
class BritneySpears
{
public:
int getValue() { return m_value; };
private:
int m_value;
};
Which is an external library (that I can't change). I obviously can't change the value of m_value
, only read it. Even deriving from BritneySpears
won't work.
What if I define the following class:
class AshtonKutcher
{
public:
int getValue() { return m_value; };
public:
int m_value;
};
And then do:
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;
I know this is bad practice. But just out of curiosity: is this guaranteed to work? Is it defined behaviour?
Bonus question: Have you ever had to use such an ugly hack?
Edit: Just to scare fewer people: I don't intend to actually do this in real code. I'm just wondering ;)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您可能无法修改
BritneySpears
的库,但您应该能够修改 .h 头文件。如果是这样,您可以让AshtonKutcher
成为BritneySpears
的朋友:我真的不能容忍这种伎俩,而且我想我自己也没有尝试过,但是它应该是合法的、定义明确的 C++。
You might not be able to modify the library for
BritneySpears
, but you should be able to modify the .h header file. If so, you can makeAshtonKutcher
a friend ofBritneySpears
:I can't really condone this trick, and I don't think I've ever tried it myself, but it should be legal well-defined C++.
@Marcelo 说得对:成员的顺序在不同的访问级别上是未定义的。
但请考虑以下代码;这里,
AshtonKutcher
的布局与BritneySpears
完全相同:我相信这实际上可能是有效的 C++。
@Marcelo has it right: the order of members is undefined across different access levels.
But consider the following code; here,
AshtonKutcher
has exactly the same layout asBritneySpears
:I believe that this may actually be valid C++.
您的代码存在问题,答案已用下划线突出显示。问题来自于对值进行排序。
然而,您几乎已经完成了:
现在,您拥有完全相同的布局,因为您具有相同的属性,以相同的顺序声明,并且具有相同的访问权限......并且两个对象都没有虚拟表。
因此,诀窍不是改变访问级别,而是添加一个方法:)
当然,除非我错过了一些东西。
我准确地说这是一场维护噩梦吗?
There is an issue with your code, underlined by the answers. The problem comes from ordering the values.
However you were almost there:
Now, you have the exact same layout because you have the same attributes, declared in the same order, and with the same access rights... and neither object has a virtual table.
The trick is thus not to change the access level, but to add a method :)
Unless, of course, I missed something.
Did I precise it was a maintenance nightmare ?
通常应避免使用reinterpret_cast,并且不能保证给出便携式结果。
另外,为什么要更改私人成员?您可以将原始类包装在一个新类中(首选组合而不是继承)并根据需要处理 getValue 方法。
Use of reinterpret_cast should usually be avoided and it's not guaranteed to give portable results.
Also, why do you want to change the private member? You could just wrap the original class in a new one (prefer composition over inheritance) and handle the getValue method as you wish.
这是未定义的行为。每个访问限定符部分中的成员都保证按照它们出现的顺序排列,但访问限定符之间没有这样的保证。例如,如果编译器选择将所有私有成员放在所有公共成员之前,则上述两个类将具有不同的布局。
编辑:重新审视这个旧答案,我意识到我错过了一个相当明显的点:结构定义每个都只有一个数据成员。成员函数的顺序无关紧要,因为它们不影响类的布局。您可能会发现两个数据成员都保证位于同一个位置,尽管我不太了解该标准,无法确定。
但!您无法在不相关的类型之间取消引用
reinterpret_cast
结果。依然是UB。至少,这是我对 http://en.cppreference.com/w/cpp 的阅读/language/reinterpret_cast,这确实读起来很粗糙。This is undefined behaviour. The members within each access-qualifier section are guaranteed to be laid out in the order they appear, but there is no such guarantee between acccess qualifiers. For instance, if the compiler chooses to place all private members before all public members, the above two classes will have a different layout.
Edit: Revisiting this old answer, I realized that I missed a rather obvious point: the struct definitions have exactly one data member each. The order of member functions is irrelevant, since they don't contribute to the class's layout. You might well find that both data members are guaranteed to be in the same place, though I don't know the standard well enough to say for sure.
But! You cannot dereference the result of
reinterpret_cast
ing between unrelated types. It's still UB. At least, that's my reading of http://en.cppreference.com/w/cpp/language/reinterpret_cast, which is a gnarly read indeed.这是未定义的行为,原因是 Marcelo 指出出去。但有时在集成无法修改的外部代码时,您需要诉诸这些东西。一种更简单的方法(以及同样未定义的行为)是:
This is undefined behavior, for the reasons Marcelo pointed out. But sometimes you need to resort to such things when integrating external code you can't modify. A simpler way to do it (and equally undefined behavior) is: