23

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

Community
  • 1
  • 1
ereOn
  • 53,676
  • 39
  • 161
  • 238
  • 14
    This code could be optimized greatly by just having `BritneySpears::getValue()` and `AshtonKutcher::getValue()` simply return 0. – Syntactic May 14 '10 at 13:21
  • @Syntactic: I knew someone would point that out :P Do I really need to add a constructor to make my question error-free ?! :D – ereOn May 14 '10 at 13:28
  • possible duplicate of [Accessing private members](http://stackoverflow.com/questions/726096/accessing-private-members) – Pontus Gagge May 14 '10 at 13:30
  • 1
    @ereOn: Oh, I'm not clever enough to point out errors in C++ code. I was referring to the fact that if these classes correctly modeled the actual Britney Spears and Ashton Kutcher, they would have to have a value of 0. – Syntactic May 14 '10 at 13:37
  • @Syntactic: If the classe correctly modeled the actual Britney Spears, *all* her **members** would have been **public** ;) – ereOn May 14 '10 at 13:40
  • 6
    Why do you want to access Britney Spears' private parts? (I'm sorry. I tried really hard, but I just couldn't resist.) – sbi May 14 '10 at 13:48
  • @sbi: I could have choose `A` and `B` but I knew it would have been less much fun :D – ereOn May 14 '10 at 13:52
  • @ereOn: And it would keep spawning child processes and performing unsafe operations with them. – Syntactic May 14 '10 at 14:23
  • Possible duplicate: http://stackoverflow.com/questions/424104/can-i-access-private-members-from-outside-the-class-without-using-friends – big-z May 14 '10 at 14:38
  • 3
    +1 for using `BritneySpears` in an example with undefined behavior. – Hugh Brackett May 14 '10 at 15:19
  • 1
    +1 for getting Britney's privates and "ugly hack" in the same sentence. – György Andrasek May 14 '10 at 16:55
  • So many geek jokes about Britney Spears: i love it ! – ereOn May 14 '10 at 18:48

6 Answers6

21

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.

Marcelo Cantos
  • 181,030
  • 38
  • 327
  • 365
  • 1
    So what If every member of `BritneySpears` was private, and every member of `AshtonKutcher` was public ? Would the order become guaranteed ? – ereOn May 14 '10 at 13:33
  • 1
    @ereOn: Only if they were all in a single access-qualifier section. Having two sections, both private, still results in an unspecified memory layout. – Marcelo Cantos May 14 '10 at 13:40
  • I still wonder how one can learn this without asking on SO. Thanks for the lesson ! :D – ereOn May 14 '10 at 13:52
  • 2
    @ereOn For stuff like this, the only way to know for sure is to read and understand the standard. – KeithB May 14 '10 at 14:21
  • 1
    Even if the members were typographically identical in each class, you still could not guarantee the actual memory layout if they are compiled with different compiler options or different compilers. – David R Tribble May 14 '10 at 14:44
  • @KeithB I still don't master english good enough to be able to read such a standard without catching a serious headache. – ereOn May 14 '10 at 18:43
  • @Loadmaster: Good observation. My answer assumes the same compilation environment. Of course, one must assume this in order to get just about any program bigger than one compilation unit to work. The problem with mixing and matching access-qualifiers is that the layout can be different even in a single compilation unit, let alone across multiple compilation units with identical environments. – Marcelo Cantos May 14 '10 at 21:48
9

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"
Community
  • 1
  • 1
Michael Kristofik
  • 34,290
  • 15
  • 75
  • 125
  • 5
    we can have private members without the label "private" so this may not work ;) – Nick Dandoulakis May 14 '10 at 13:38
  • @Nick D: Works on structs though (?) – Tom May 14 '10 at 13:41
  • 3
    @Nick: `#define class struct`. Not that I'd ever do anything like that. – Mike Seymour May 14 '10 at 13:43
  • This still doesn't solve the problem of object layout. If the library was built with a different compiler, then the layout of the `BritneySpears` class in the library is already set: `m_value` is in the `private` section, and there's no amount of messing with the header that will change that. So although you can trick your compiler into building your code with this hack, you still have the problem of incompatibility if your compiler and the compiler used to build the library do not lay out their `public` and `private` sections the same way. – Tyler McHenry May 14 '10 at 13:43
  • By the way, I know that you (Kristo) understand that this is undefined behavior, I just thought that this was worth pointing out explicitly. – Tyler McHenry May 14 '10 at 13:45
  • 1
    @Tyler, that's good info. Although, aren't you pretty well hosed in general if you mix compilers like that? C++ doesn't have a defined ABI after all. – Michael Kristofik May 14 '10 at 13:47
  • @Mike, oh yeah, `#define class struct`, `#define private public` is a lethal weapon! – Nick Dandoulakis May 14 '10 at 13:49
  • Add #define protected public – Demi Dec 07 '13 at 20:04
  • 1
    You are not allowed to #define c++ keywords, thus `#define private public` isn't legal c++ – JarkkoL Aug 31 '14 at 03:04
4

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

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
2

@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++.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • Well, we have no guarantee that the compiler will order the different sections in the same order for two different classes. Well, I can't see which implementation would act like that, but still. – ereOn May 14 '10 at 13:43
2

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 ?

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
1

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.

kgiannakakis
  • 103,016
  • 27
  • 158
  • 194
  • Well, I was just wondering. I don't intend to this in any real code ;) – ereOn May 14 '10 at 13:30
  • Since the method `getValue` is not virtual, you will not be able to change its behavior in existing code by extending the class (all existing references at base level will call the base method, and the method --not really overriding-- in the derived class will never be called) – David Rodríguez - dribeas May 14 '10 at 13:47