views:

356

answers:

7

I want to access a private data member in a class. There is no member function in the class to access the private data member. It is private.

I want to take the class and some how crack it open. One method was to copy the declaration of the class, make the private member public and call the new class class something_else. Then I do a reinterpret cast and copy the original object. This works. But I want something more elegant ... or perhaps generic ... or just another way.

What options are there? Can I use void*? Can I memcpy the class into another empty class? What are ways to do this??

%

+16  A: 

I am assuming that

  1. You've already been through "breaking encapsulation is bad" stage,
  2. Exhausted other possible solutions,
  3. Can't change class' header.

There are a few ways to subvert access to a class's private members, as demonstrated in GotW #76.

  1. Duplicate a class definition and add a friend declaration.
  2. Use evil macros: #define private public before including class' header.
  3. Write a class definition with identical binary layout and use reinterpret_cast to switch from original class to a fake one.
  4. Specialize a template member function if there is one (the only portable solution).
Alex B
+1. WONDERFUL article.
LiraNuna
Love the evil macros mention ;)
Calyth
+1  A: 

You can, but you shouldn't. The objects are just memory. You can certainly cast the pointer into an equivalent class that has the same members but where everything is public. But why do you want to do this? Do you have somebody else's code that you need to work with? Get them to add proper accessor methods. Do you really need to treat them as public members? Change the class.

I'm not really sure what you are trying to do, but it's probably a mistake.

vy32
A: 

I agree with the "edit the source" comment, but I think you should add a method, not just comment out the 'private'.

You must have the declaration of the class, so you presumably have the header but possibly not the .cpp/whatever file. Add an inline member function to the class in a copy of the header, and include this header instead of the original. You should still be able to link to the object file for the inaccessible source code.

Of course this counts as an evil hack, bypassing the protections built into the language rather than working with them. That's why I suggest the minimally evil hack - don't make everything private, and if you can get away with a getter (but no setter) do that. Of course the real minimal evil is not to do it, if there's any way at all to avoid it.

Remember, if this is someone elses class you're working with, the next version might be implemented differently and might not have that member at all.

Steve314
My comment was also portraying that there's a design issue somewhere...
LiraNuna
+3  A: 

With the idea you suggest in your question, you don't need to copy the original object. If you write your own "all public" variation of the real class declaration, then cast a pointer to that new type, you can directly access the object through it.

The reason why none of this is a good idea is simple. You must be manipulating objects of a class of which you don't control the source (otherwise you'd be able to modify the source to give you the access you need). But if you don't control the source, then what if the maintainers change the layout of their class? Your duplicated version will no longer match up, and there will be no way for the compiler to detect this mismatch. The result will probably be memory corruption at runtime.

Daniel Earwicker
+3  A: 

Since it's incorrectly understood, I have to clarify. All the following solutions do not require you to recompile the object. To use a class in your code, if it's compiled into an object file, you should include header file with the declaration of that class.

#include <class.h>
ObjectFoo instance;

It is possible (but dangerous unless you're careful) to change the header (a) or copy the header to another place and include that header (b), without recompiling the class itself.

#include <class_fixed.h>
ObjectFoo instance;

Your code, where you included the new header will just think that within the object file (which you haven't recompiled!) he will find implementation of the class declared as in class_fixed.h. While there persists the class declared as in class.h. If you change offsets of members (add new members for example) in your new header, you're dead and the code will not work properly. But just changing the access works fine. Compiled code doesn't know about access, this matters only at the compilation strange.

This is not always harmful. In everyday life you encounter such a change when you install new version of a library into your system and do not recompile all programs that depend on it. But it should be handled with care


There are several solutions.

  1. memcpy()
    Don't! Do not memcpy as object copying sometimes undergoes specific policy imposed by the class designer. For example, auto_ptrs can't be just memcopied: if you memcopy the auto_ptr and then destructor is ran for both, you'll attempt to free the same memory two times and the program will crash.

  2. Change private: to public: in header or with macro
    If your license permits it, you may solve your problem by editing the header file that comes with the implementation of the class. Whether the source code of the implementation (i.e. cpp-file of the class) is under your control doesn't matter: changing private to public for data members (in header) suffices and works just fine even if you're given a binary-only library that contains class definition. (For member functions changing access sometimes changes its internal name, but for MSVS and GCC it's ok.)

  3. Adding a new getter function
    While changing private to public is nearly always ok (unless you rely on specific compile-time checks that should break the compilation if class has certain member accessible), adding new getter function should be performed carefully. The getter function should be inline (and therefore defined in the header file of the class).

  4. reinterpret_cast
    The cast works just fine if you're NOT casting a pointer to dynamic base class (dynamic means "with virtual functions or bases") whose actual instance at the moment of casting can be derived from the class at the particular piece of code.

  5. protected:
    And just in case you forgot. C++ can declare members protected:, i.e. accessible only to the classes derived from the given. This may fulfill your needs.

Pavel Shved
A: 

Thank you ... I did want to show the code for my original fix. The reason as someone aluded to is that I cannot change the original code ... so I have to do a jail break.


#include<iostream>
using namespace std;

// Class Objectfoo
// Pretend Objectfoo lives somewhere else ... I cannot open him up

class ObjectFoo 
{
  private:
  int datax; 
  public:
   ObjectFoo() { datax = 100; }
   void get() { cout << datax << endl;}
};

// Class ObjectBar
class ObjectBar 
{
  public:
   int datax;
};

ObjectFoo FOOEY;

ObjectBar* touch_foo(int x, ObjectFoo* foo , ObjectBar* bar)
{
 bar = reinterpret_cast<ObjectBar*>(foo);
 bar->datax = x;
 return bar;
}

int main() 
{
  ObjectBar* bar;

  cout << "Displaying private member in ObjectFoo i.e. ObjectFoo.datax" << endl;
  FOOEY.get();

  cout << "Changing private member " << endl;
  bar = touch_foo(5, &FOOEY, bar);

  cout << "bar->datax = " << bar->datax << endl;

  cout << "Displaying private member in ObjectFoo i.e. ObjectFoo.datax" << endl;
  FOOEY.get();

  return 0;
}


This works ... but I think I want something more generic ... or more flexible.

%

Xav
Many answers here assumed you can't change the original `.cpp` code, while you can change--or just copy, rename and reinclude--the **header** file. Re-read the answers with this insight.
Pavel Shved
It is much better to add this information to the original question rather than adding it as an answer.
KeithB
A: 

Please note:

  1. I have the headers and I am adding new capability to a legacy code.

  2. I cannot change the original code. It is legacy code that must stay the same.

  3. So I must do the trick.

  4. I can't add a member function to the original code, or change its access. It is compiled ... I can't touch it. I just need to set a ONE variable.

  5. I cannot use templates. I just cannot for this fix. Not allowed.

As an aside ~ I am curious if I can create an object by memcpying it.

%

Xav
I edited my answer to explain these matters. I hope you re-read it.
Pavel Shved
It is legacy code that must stay the same, or it is legacy code that must be compiled in a binary-compatable way?
Bill