God damnit, I just lost my long post. I'm not motivated to retype it all, so I'll keep it short.
I think I'll just sum up what the main problem is: You keep thinking that designing an API where a lot of possible calls that the user can make are actually invalid, and won't result in a compile-error or handle-able error, but just in unexpected behaviour. Not only does this go against the principle of least surprise, where every procedure should do exactly what you can expect of it. Its just a wrong approach in my opinion, you shouldn't make it easy for the user to fuck up if he doesn't follow your exact idea about the code, but you should make it hard to fuck up for the user if he just follows what the API allows him to do. Thats why we have const-correctness, thats why we have RAII, thats why we pass by & instead of *, etc, etc.
In all honesty, would you rather work with an API where you
A) Can assume that almost every public method/data-member can be called and written to, without unexpected side results, if you adhere to the general function signature.
OR
B) Can assume absolutely nothing about what is an valid call or not. If you want to write a value, unless you know the library at heart, you have to make sure that writing to it is actually valid.
And yeah, if you begin making read-only members public because you don't feel like writing a getter (pure setter/getter pairs as I said can be omitted in most cases), then the actual worse thing that happens is, that a user cannot assume anthing. You are not designing by contract anymore, you are designing by "you have to know exactly what you are fucking doing, otherwise you are screwed!".
I actually lost my whole detailed answer to your points, but here I also realized that it pretty much doesn't matter anway since every point I would issue, because every concern about safety seems to just result in "why does it matter" to you? But anyways, here is some details:
- I actually recall a bad situation in Unreal, where you could both set a bReplicated-member, as well as Call SetReplicated. SetReplicated performs additional work, and setting bReplicated = true doesn't work outside of the constructor. This lead to some nasty replication bugs (stuff just wasn't replicating and we didn't know why), which resulted in an extremely long, pointless bug hunt. As for your question "Is it really hard not to write bReplicated = true"? Yes it is, if you haven't designed the library yourself or worked with it a tremendous amount of time. It shouldn't be possible to call bReplicated = true, if it doesn't work, PERIOD.
- Getter-only access does another thing for pointers. If a raw pointer is a public member, users cannot only modify the pointers content, but also the pointer itself, which most of the time is probably not what you expected. Having char* GetData() allows a user to write to the pointer, without changing it.
- This leads to an important point, consistency. If you adhere to strict access rules, it is imminent how to handle values. If I have a public pointer member, I can set it, not matter what. If I have a public property, I can set it. This not only reduces space for errors, but also increases productivity. Yes, having to check the documentation for every single data member/function is a very bad thing. No-one will remember what is allowed to be accessed and what not without a huge amount of time spent.
- Actually I'm kind of beginning to feeling a tad bit shizophrenic with this whole discussion. Access-modifiers have been invented to restrict access, and you are just like "some of my publically accessibly data members are actually not fully publically accessible". ...
- The point about future extention of a class.
Deciding what data may or may not be seen for other classes that don't exist yet, is pure speculation. You're making decisions that may not hold at all, how do you know what you decide is correct?
I'd put it the other way around: You design your classes with a clear purpose in mind. SOLID-principles in mind, you design your classes with a single, well defined responsibility, and make it open for extention, but close for modification. That way, future classes shouldn't need to access data other than what you expected - you just designed your class to handle itself in terms of mostly input/output, and not state. Well thats another point per se, but worrying about future classes uses is a strong case of YAGNI.
Well as I said as far as I can see, your point boils down to "Its absolutely fine to have an unsafe class API", to which I pretty much can only say "no, its not", for all the points I listed. Yes, its not possible have eigther extrems (a rock-solid vs. a totally unsafe API), and in reality you have to take trade-offs, but you should definately aim towards making your API safe for use per se, instead of "requires intensive knowledge to be safe".
Though you are kind of putting me off by at one point saying you are hiding class internals, and then on the other hand saying that its ok to have some internals write-accessible even though unsafe (like position, which is dependant).
Though I'm laking a lot of work-experience, I'd be glad to hear what some other professionals have to say about this. Though I feel this is drifting away from what the OP originally wanted to know, so maybe this should be moved to its own thread, if the discussion should continue.