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?
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.
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++.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With