Sign in to follow this  
phil67rpg

oop question

Recommended Posts

private data-members are used if you want hide those data members from other classes or the user himself.
public members are used if you "don't care" what happens to your member.
easy said...
but it's possible to explain it more specific

Share this post


Link to post
Share on other sites

My rule of thumb is this:

I use classes whenever I have logic to manipulate my data members. Then all the data members are private, and I have setters/getters for them. For example, CVertexShader() - it has logic for creating, destroying, setting a vertex shader, and I don't expose any of it's internals. This allows better abstraction, and if I ever need to change something with vertex shaders, it keeps the changes localized.

 

For data containers I use structs and makes everything public. Basically, stuff that I don't need to manipulate, just store and retrieve data. In that case setters/getters are just a hassle. If at any point this data starts to have logic - I convert it into a class.

 

Putting it all together - my constants buffer objects are classes with private data members, but the CBs data containers are structs. This allows my UI thread to easily change the CBs data, while hiding all the API logic inside a class.

Share this post


Link to post
Share on other sites

Formally, each class has an implicit property called class invariant. It's the assumption you can always make about each object instantiated using that class.

In practice, we must ensure each object is valid - invariant is satisfied - and you can guarantee this only by taking each modification attempt and verify it transitions your object to another valid state.

Of course, you cannot do that on stuff that is directly assignable. So, as above, public members are "don't care".

In line of concept, you should use private on all other cases. But in reality protected is often a good compromise, due to specificity of derived class code - if someone is writing a derived class, he has to deal with much more focused context than the whole program. Someone think protected is bad and to a certain degree I agree with them but in the real world, I find it very convenient.

 

Edit: derided class code... what a typo!

Edited by Krohm

Share this post


Link to post
Share on other sites

Theoretically everything outside a class shouldn't need to know about its data, thats why they are private by default. Using public is like exposing the innards of the class to the whole world.

If following "tell, don't ask" principle you would also just call a method to tell an object what to do; not use a getter to inspect it, do some outside decision and use a setter to change it from outside.

Edited by wintertime

Share this post


Link to post
Share on other sites

You want to make variables in classes private, ALL the time. But what you want to do is make an Accessor Function, or a "getter". This will allow you to obtain the variable even if it is private. But make sure you make the functions public, so you will be able to access them. You can also use a setter function, this will allow you to set the value of the private variable, but it is a public function, so you will be able to access it.

Edited by GearTreeEntertaiment

Share this post


Link to post
Share on other sites

I don't agree.

 

If there are no invariants to worry about, and the implementation is simple and obvious (i.e setting a variable and doing no verification of validity), making something public is fine.

 

I wouldn't have setX, getX, etc. for a 3D vector class, since all values are valid.

 

If there was any reason to limit the bounds of a 3D vector (e.g. physics constraints on world size), then the implementation is no longer simple and obvious so then get/set methods would be appropriate.

 

All pointer members should be private though.

Edited by Paradigm Shifter

Share this post


Link to post
Share on other sites


I wouldn't have setX, getX, etc. for a 3D vector class, since all values are valid.

 

Not to be nitpicky here, but what about values like NaN or infinity? NaN could definitely break the conditions for satisfying the class invariant for a vector class

It's all about design of course; I don't use mutators and accessors in my vector classes either since you should be able to avoid cases like NaN quite easily by just using common sense

Share this post


Link to post
Share on other sites

That's handled by the floating point specification, and adding checks is going to affect performance.

 

You could put debug only checks in, then you would use an accessor and inline it. I wouldn't do that for a vector class though. I have used it for things like verifying arguments are within expected ranges e.g. for inverse trig functions.

Share this post


Link to post
Share on other sites

You want to make variables in classes private, ALL the time. But what you want to do is make an Accessor Function, or a "getter". This will allow you to obtain the variable even if it is private. But make sure you make the functions public, so you will be able to access them. You can also use a setter function, this will allow you to set the value of the private variable, but it is a public function, so you will be able to access it.

Don't just blindly make every class member private with an accessor; sometimes there should be no accessor, and sometimes public access makes more sense. The sensible thing to do is actually consider the invariants and intended usage of your class and make members private and/or provide accessors when it's genuinely the most appropriate design.

Plain setters are sometimes valuable, but not always the best idea; it often makes more sense to provide a semantically relevant function that might adjust multiple values rather than a simple setter - for example, rather than providing setters for x and y values you might provide a single move() function that sets (and perhaps validates) both values.

Share this post


Link to post
Share on other sites

Agreed. A class should represent an interface rather than thinking in terms of their data members (which clients should not need to even know about).

 

In the vector 3D case though, the representation IS the interface.

 

You would use accessors for simple and obvious access functions if you think there may be a need for them later on though. (I've seen arguments saying it is easier to do a text search for an accessor rather than a member, however I dismiss that since you can comment out the member and the compile errors tell you where the member is used).

Share this post


Link to post
Share on other sites

That's handled by the floating point specification, and adding checks is going to affect performance.

 

You could put debug only checks in, then you would use an accessor and inline it. I wouldn't do that for a vector class though. I have used it for things like verifying arguments are within expected ranges e.g. for inverse trig functions.

 

Again, not to be difficult here since I agree with your opinion on the subject completely, but if you want to look at it formally wasn't the concept of class invariants introduced to avoid cases where you can get unexpected or undefined behaviour? I'd assume that some floating point specifications would show undefined behaviour when doing operations on NaN values which would be an issue if you want to be really strict about your OO design.

 

And yes, checks would affect performance, but there will always be a difference between writing code which can be proven to be correct and writing highly performant code.

Share this post


Link to post
Share on other sites

Floating point specs are perfectly well defined for operations involving NaNs and infinities. It wouldn't be much of a specification if they weren't! (Basically once you get to NaN or infinity values there is no going back to "real numbers").

 

For non-conforming implementations you need to check the compiler documentation (non conforming stuff usually only affects things like rounding modes and precision rather than behaviour of NaN and infinities though). If conformance is required you can usually enable it (and take a performance hit).

Share this post


Link to post
Share on other sites

 


there will always be a difference between writing code which can be proven to be correct and writing highly performant code

I disagree. Highly performant code can and should be correct. Always.

 

 

There is a very big difference between "The code should be correct" and "The code is proven to be correct".

In the second case it is impossible for a bug to exist in the code, in the first case testing hasn't discovered any bugs (yet).

Edited by SimonForsman

Share this post


Link to post
Share on other sites


There is a very big difference between "The code should be correct" and "The code is proven to be correct".

In the second case it is impossible for a bug to exist in the code, in the first case testing hasn't discovered any bugs (yet).

I don't see why performance has any bearings on it. If you can write crappy-debug code that is proven to be correct, and you know how to optimize, then you can write highly efficient and optimized code that is proven to be correct.

Share this post


Link to post
Share on other sites

 


There is a very big difference between "The code should be correct" and "The code is proven to be correct".

In the second case it is impossible for a bug to exist in the code, in the first case testing hasn't discovered any bugs (yet).

I don't see why performance has any bearings on it. If you can write crappy-debug code that is proven to be correct, and you know how to optimize, then you can write highly efficient and optimized code that is proven to be correct.

 

 

Debugged code is not code which has been proven to be correct.

We're talking about a formal proof here, and formally proving that an algorithm or procedure is correct (ie. it generates a correct output for each input) is not a trivial task. You can debug the crap out of a piece of code, but if you haven't shown that this piece of code provides a correct output for every possible input then you haven't proven this code to be correct.

 

These kinds of proofs are very important when doing design by contract for example. In critical applications it's an absolute must that you have a guarantee that your code will behave correctly in any situation.

Share this post


Link to post
Share on other sites

I understand, and I agree that it is not a trivial task, especially if you need high performance.

It doesn't mean you can't write high performance code that is always correct.

 

I was writing up a huge wall of text, but decided it wasn't worth it in the end.

 

Let me explain what I meant with my original statement:

 

The point I'm trying to make is that high performance is often paired together with designing with the hardware in mind, not the programmers.

You'll often be skipping over some checks (ie. invariant enforcements) or exposing some internal state to gain a performance boost while making the assumption that anyone using the code will be "smart" enough not to provide nonsense input values. This in turn will make it impossible to prove your code to be correct since you can't guarantee you'll conform to your class invariant at all times.

Share this post


Link to post
Share on other sites

Well the public interface should maintain the invariant of the class. Non-public methods can do what they like though since they are only used to implement the public interface.

 

If you must break an invariant you probably want a class that wraps the badly behaved class. (e.g. say we have a requirement you need to call BeginFrame before Draw calls and EndFrame afterwards, you would probably assert in debug mode that the calls are made in the correct order and then wrap the whole lot in a SceneRender class). Simple example off the top of my head...

Edited by Paradigm Shifter

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this