C++ Is private really private?
Asked Answered
A

9

19

I was trying out the validity of private access specifier in C++. Here goes:

Interface:

// class_A.h

class A
{
public:
    void printX();
private:
    void actualPrintX();
    int x;
};

Implementation:

// class_A.cpp
void A::printX()
{
    actualPrintX();
}

void A::actualPrintX()
{
    std::cout << x:
}

I built this in to a static library (.a/.lib). We now have a class_A.h and classA.a (or classA.lib) pair. I edited class_A.h and removed the private: from it.

Now in another classTester.cpp:

#include "class_A.h"    // the newly edited header

int main()
{
    A a;

    a.x = 12;           // both G++ and VC++ allowed this!
    a.printX();         // allowed, as expected
    a.actualPrintX();   // allowed by G++, VC++ gave a unresolved linker error

    return 0;
}

I know that after tampering a library's header all bets are off (I mean, system integrity, etc.) Albeit the method being hacky, is this really allowed? Is there a way to block this? Or am I doing something wrong here?

Aldric answered 16/2, 2010 at 13:16 Comment(5)
I know that the Cheshire Cat (Pimpl - private impl. en.wikipedia.org/wiki/…) design is possible here and also that access specifiers are compile-time guards by the compiler.Aldric
Why on earth can't you just rebuild it?Photodynamics
@Dominic: My intent was to see what happens if I'm trying to access private parts of a class.Aldric
private members are still present in memory. Of course you can access private data with enough hackery. And of course it's not "allowed" by the language. But the language or the compiler can't prevent you if you want to cheat.Jojo
@jalf: Thanks jalf, all in all, I've understood that by doing this, the client user of the library enters into undefined arena; that is a reason good enough not to do this. Gagge's answer made it clear that this isn't a security mechanism but a intent specifier.Aldric
S
30

private is not a security mechanism. It's a way of communicating intents and hiding information that other parts of your program do not need to know about, thus reducing overall complexity.

Having two different header files is not standards compliant, so technically you're entering undefined behaviour territory, but practically, as you've found, most compilers won't care.

Sunstroke answered 16/2, 2010 at 13:18 Comment(0)
I
9

You've strayed beyond what's allowed in C++, so what you're doing isn't allowed - but of course it may work on some compilers in some situations.

Specifically, you're violating the One Definition Rule.

This article by Herb Sutter explains it quite nicely - it also provides a legal and portable way of circumventing the access specifier system.

Indreetloire answered 16/2, 2010 at 13:22 Comment(3)
But I've not defined anything more than once; Agreed, I've tampered the declaration, but how come it becomes a case of ODR violation?Aldric
You have defined class A twice - once in the classTester.cpp translation unit and once in the class_A.cpp translation unit. The ODR violation is that those two definitions aren't indentical.Indreetloire
Aargh, I see it now! Thanks for Sutter's article on the same.Aldric
A
7

No. The private access control is there to stop YOU from doing stupid things, not as a security mechanism to stop others accessing your data or functions. There are many, many ways of getting around it.

Autogamy answered 16/2, 2010 at 13:18 Comment(0)
F
5

I tried. This is an nm of a program I wrote having a class Test with one private method and a public one.

0000000100000dcc T __ZN4Test3barEv
0000000100000daa T __ZN4Test3fooEv

As you can see, the signature is exactly the same. The linker has absolutely nothing to distinguish a private method from a public one.

Fab answered 16/2, 2010 at 13:32 Comment(1)
Yes, like I've mentioned, in G++ there was absolutely no error for accessing actualPrintX(); but the symbols in VC++ seem to have some difference and hence the linker threw an error. Thanks!Aldric
T
5

I agree with most of the other answers.

However, I'd like to point out that it is perfectly acceptable for a compiler to physically arrange the members differently when you remove that private. If it works, that is luck. You can't count on it. If both sides aren't using the same declaration, they aren't really using the same class.

Thickskinned answered 16/2, 2010 at 13:51 Comment(0)
T
2

Since noone has mentioned a way to block this... One possible way to block access to private members, is to declare them as a separate internal type not visible outside the file. Of course, if you want to provide explicit access to this internal, you would then have to provide the internal declaration. That's also commonly done using an internal header containing that type.

Note: You'll have to keep track of allocating/freeing that internal object in this example. There are other ways of doing it that don't require this.

// class_A.h
class A {
public:
  void printX();
private:
  void *Private;
};

// class_A.cpp
class A_private {
  void actualPrintX();
  int x;
};

void A::printX() {
  reinterpret_cast<A_private *>(Private)->actualPrintX();
}

void A_private::actualPrintX() {
  std::cout << x:
}
Turnheim answered 16/2, 2010 at 14:23 Comment(2)
Likewise, next to the question, in the comments, I've mentioned about the Cheshire Cat method too, which is similar to this, only without the reinterpret_cast.Aldric
Pardon me, I must've missed that; good to know it actually has a name.Turnheim
B
1

There is no A::actualPrintX implementation anywhere. That is your linker error.

Batt answered 16/2, 2010 at 13:19 Comment(2)
Thanks for notifying, it's a typo, I've fixed it now :)Aldric
Guys, please don't down vote him. Earlier in the question, I've just put actualPrintX() instead of A::actualPrintX(); he just notified me of that typo.Aldric
D
1

In most cases you don't even have to edit header file to make private members public. You can do this with preprocesor. Something like this:

//"classWithPrivateMembers.hpp"
class C
{
private: //this is crucial for this method to work
    static int m;
};

int C::m = 12;

and then, this will work:

#define private public  
#include "classWithPrivateMembers.hpp"  
#undef private

int main()
{
    C::m = 34; // it works!
}
Deed answered 16/2, 2010 at 19:21 Comment(1)
You can also add #define class struct to catch unqualified private vars.Donar
S
1

Bear in mind also that when you change a member variable's access, the compiler may place it at a different offset within the class object. The standard allows compilers a fair amount of freedom in rearranging members (at least within the same access level, I think).

Spriggs answered 16/2, 2010 at 19:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.