Unless you use the pimpl idiom, data hiding is imperfect in C++ because the user of the class must be able to see the private members at compile time to correctly generate calls into the class and access public members, so changing private implementation details can require recompilation of user code. In other words, all parts of the class are part of the interface, even if the language does not allow some parts to be used at all.
Compare this to for example C#, where only the public members of the class are part of its interface, and so only when such members change is recompilation necessary.
When you say playing around with the pointer is 'unsafe,' what exactly do you mean? It's discouraged or not allowed? |
An unsafe operation is one that may fail[1] and it's not possible to know ahead of time if it will. Often, there are procedures that the programmer is expected to follow that can ensure that an otherwise unsafe operation becomes safe. Some examples of unsafe operations:
* Dereferencing an invalid pointer.
* Returning a pointer or reference to a local.
* Accessing elements outside the bounds of an array.
* Simultaneously modifying a container in two separate threads without synchronization.
* Non-atomically incrementing a variable in two separate threads.
It's not necessarily true that accessing private members of an object by directly using the object's memory is unsafe. If you happen to know what kind of code the compiler generates for a given class, it's perfectly safe. The code will not mutate while the program is running (if it does, it's a different story).
As to the unique_ptrs, I thought it would throw an exception or something if another pointer tried to point to the same address? |
This is not possible, because a pointer is just an integer whose value happens to be used by the program in a specific way (to access memory). An integer or even a float could by chance be assigned the same value as a real memory location, and there would be no way to prevent someone from using that value to access that location. Doing that would require scanning the entire address space and all CPU state every time the program state changes. This is of course impossible from a program that's running on the same processor.
[1] In this case, an operation is said to "fail" if it doesn't alter the program state in a manner that is consistent with the operation's postconditions. For example,
++x;
is expected to cause x@after == x@before + 1 to be true. The operation has failed if it's not.