开发者

Is private member hacking defined behaviour?

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 开发者_StackOverflow中文版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 ;)


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_casting 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.


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:

#define private public
#include "BritneySpears.h"


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 make AshtonKutcher a friend of 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; }; 
}; 

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 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 as BritneySpears:

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;
}

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:

class AshtonKutcher
{
public:

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

private:
  int m_value;
};

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 ?


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜