display | more...

Ooh, very very bad OOP practice. You definitely dont want other classes messing around with your member variables. This is what OOP is all about - data hiding and the like. And those get*() and set*() functions really grow on ya (not to mention that they keep everything looking clean).

There's really no good reason to make variables in a C++ class public1. If you can't understand why, then you simply don't understand the theory behind OOP.

It's the idea of separating the interface from the implementation. By allowing outside code to access the class's variables, you're tying the use of that class to that specific data representation.

For example, imagine that you write a Stack class (rather pointless, unless you WANT to duplicate the functionality of the STL, but anyway...), and you implement it with an array. You document that the correct way to access the top of the stack is to read the value of stack.array[stack_top]. But later on you discover you need the stack to be able to grow and shrink at will. The best way to do this, of course, is to use a linked list. But you can't use a linked list, because all the code that uses your class directly reads your class's private members, so you can't change the representation of your data!

Not to mention that using accessor functions (the get_*() and set_*()) allows you to perform sanity checks, for things like overflow, underflow, valid input, etc.

1Constants are a different story, especially static ones.

While I was rambling, it seems that others have responded first... But at least I gave a different example of why it's useful -- there are a lot of reasons...

You make data members private to preserve consistency of state. This matters. It matters a lot. You also do it to allow the implementation of a subclass to differ drastically from that of the base class: This is called "abstraction", and it's a big part of the reason why we bother doing OOP at all. So let's take those two in order:

Say for example you've got a class which represents a list of strings. You've got an interface for that class: A count of the number of strings you've got, a number which says how much memory you've got allocated1, and you've got a pointer to the storage: We'll assume for the sake of simplicity that it's an array of pointers to your favorite string class.

Okay. Take for example that number which tracks the size of your array. Assume that some genius comes by and changes it without reallocating (this could be you two weeks later, or even at 3:00 AM the following morning). Okay, you crash. But not always. Bugs like that are a bitch to track down, because you won't crash consistently. Sometimes it'll be a problem, sometimes not; you may get away with using the next few bytes off the end of your array, but when you call realloc() they won't get dragged along with the rest. It's a mess. Don't go there.

The same goes for the data member tracking the number of strings you've got: If it says "five", and you change it to "six", your code will eventually try to call member functions on an object that doesn't exist: p5->size(), for example. You're in trouble again.

In inline function in C++ incurs no overhead, unless it's a virtual (but if you need it to be virtual, you need a function anyway, so you're stuck). Use them.

Of course, there are some public data members which can safely be changed by the "user" -- but which is which? Here's a way to make it easy: If the "user" shouldn't touch it, hide it.

Okay. Now let's assume that you've got code here and there which uses your string list class. You've got other lists of strings which you might like to interact with also, but they can't be stored in that list thing: Say you've got a GUI widget such as a list box. Well, it's got a list of strings, hasn't it? How many sets of functions do you want to write to do the same thing to different classes? My answer is, "one". All lists of strings should have the same interface if at all possible. It's less code to write, less hassle to deal with.

So. Let's say your string list class has an abstract base class (no data members at all, just a pure functional interface, without even bodies for most of the virtual functions (In C++ a bodiless virtual function is called a "pure virtual function") ). If that's the case, you can make one subclass which contains the above-described list of pointers to string objects, and another subclass which provides an interface to the guts of a list box widget. You can interact with both in exactly the same way, using the same code: Just declare a pointer to the abstract base class. Java provides a similar and somewhat simpler feature called "interfaces"2.

The above list/widget example is taken from Borland's VCL library, shipped with their Delphi and C++ Builder products. It's a neat idea. It's the first example that sprang to mind, but there are dozens of others.

In a nutshell: If you don't know why you should use abstraction, you don't know object oriented programming and you don't know C++. This is generally true of all C++ features: If you can't see why on earth anybody would bother, trust me, you're missing something important. This is the same as making the transition to C from an unstructured "goto language" like Basic: Functional decomposition seems kinda silly at first. It really looks like gratuitous rigamarole until you begin to grok. Then grokking happens, and you wonder how you could ever have done it any other way. So it is with OOP.

I recommend learning: It's enormously fun stuff.



1 More than you need, if you've got any sense: If you allocate more storage than you need, you can add more items to the list without having to reallocate each time.

2 . . . which you should learn about and think about because there is wisdom implied in what they did with it and in its intended uses.


In core10k's example, he forgets to return a const reference to the cout object, and he forgets to make the function an inline. That's not the way a C++ programmer would have written it. More to the point, is that function being called a thousand times in a row, in a tight loop? If not, you may be sacrificing an awful lot just for a small optimization on something that only happens once in a while. It's not a very good example. There are good examples. The point is a valid one: Abstraction is not always and forever the only way to do things; there is often a cost, and sometimes it costs too much. it's just the best way in most normal cases, for the reasons given above.

For those who are concerned about C++ having serious efficiency problems, I recommend The Design and Evolution of C++ by Bjarne Stroustrup. It's worth reading if you either use C++, or have avoided using it.

Furthermore, changing things in the future isn't a "luxury", it's a law of nature in software development, at least in my experience. Sometimes you have to make sacrifices for the sake of efficiency, but you do it with a profiler and be sure you're getting something back for your troubles.

Let me start by saying that all of my comments apply only to application level code. There is never an excuse any more to not follow standard encapsulation practice when writing library code.

This is one of those things where a little more effort up front pays off big later. Unfortunately, there are fairly strong incentives to not follow good practice, and you've tripped on one of the biggest.

At one level, code is often easier to read when you don't abstract away the representation. This is because you remove a level of indirection and bring the code one step closer to the hardware. Not having to wade through a sea of member functions to understand a module you are debugging is sometimes nice.

Plus, it's faster to write. Hiding the structure of an object forces a much different style of programming that--there is no way around it--takes longer than the equivalent functionality without the abstraction.

But, you pay for it later. Anything you write that is truly useful is going to provide functionality that you eventually want to either automate or embed in other applications.

If you've followed good OOPy1 procedure and hidden away the representation of your application, your user interface, and your state then it will be relatively easy to adapt your code to its new environment. You won't have to worry that today a FrobNazz is a string, but later needs to be a map<String, NetworkObject*>. You won't have to wonder which security critical function is going to try to read through a null pointer after the change.

On the other hand, if you've gone the easy way, you get to essentially rewrite the whole thing every time you need to bring it up on a new architecture, localize it to a new language (human language, not programming language), or embed it into another new system.

1- Referred to as gettin' oopy with your app

It should be noted that some progamming languages have managed to solve this ugly little issue. I'll use Ruby for the example. The solution entails:

  1. Remove any form of public data members belonging to an object.
  2. Make it unnecessary to use parentheses to call methods. This would be for infix-style languages.
  3. Allow a "writer" (setter) method type. For example, named "bar=".

For example:
class Foo
    def initialize
        @bar = nil # Create a instance variable
    end
    def bar
        @bar # This is the accessor
    end
    def bar=(x)
        @bar = x # This is the writer
    end
end

foo = Foo.new
foo.bar # returns nil
foo.bar = 'blah' # calls "Foo#bar=", returns "blah"
foo.bar # returns "blah"

# This shortens to, for simplification when you need ONLY setting/getting...
class Foo
    def initialize
        @bar = nil
    end
    attr :bar # creates "Foo#bar" and "Foo#bar=" methods automatically
end

Given a more flexible language, doing full OOP is not painful. In a purely OO language (this case, utilizing the nonexistance of public variables), it won't be as ugly as the C++ hackery with foo.bar() and foo.set_bar(). (Remember, pointing out flaws, like non-full-OOP in favor of C backward compatibility, is not flamage.)

Undoubtably always a bad idea -- EVERYBODY on this node says so.

Now all that remains is for the snow to begin to explain STL. It has the structs:

std::pair<T1,T2>, with public members first, second.
In case you might think it's unused, std::map and std::multimap both return this from (in their operator[] method).

The often overlooked benefit in data hiding or encapsulation is that objects are a whole lot easier to debug than are big gloppy strands of spaghetti code referring to the member variables of hundreds of classes. For example if you know that only way a variable can change is by way of an accessor function, then you can set a breakpoint at that function. Each time the breakpoint is taken it's a simple matter of examining the call stack and finding out why the variable was being changed.

One of the problems I run into a lot these days is people who could think in an object oriented fashion. If a developer can not figure out a reason to hide the data, then it's probably not a good class of candidate, period.

Don't make a class out a group of related variables just because you can! It's just a structure or a map or a collection. It's not an object, ok? Objects handle all of their own member manipulation. This includes reading them from a database, writing them to a database, displaying them in a dialog box, performing complex calculations involving the data, etc.

Contrary to popular belief, it isn't always a bad idea to have public member variables -- there's no value in forcing your implementation details to go through get/set pairs or any such nonsense.

For instance, consider a linked list class. A linked list is made up of nodes. A typical linked list class will have a private node type. Something along the lines of:

// a list of Elems
template <typename Elem>
class List
{
    struct Node
    {
        Node* next;
        Node* previous;
        Elem value;
        // then some implementation for the node, to
        // handle insertions and deletions.
    };
    // and then the rest of the List's implementation.
};

Requiring the use of accessors for the various parts of the node is of no value whatsoever. The only people who can access the Node class are the people writing the List class -- and if you can't trust them to use the class properly, you can't trust anyone, and might as well not bother. You don't expose your Nodes to any users of the class, so it doesn't matter to them how it's implemented -- after all, the point of using OO is to keep these details hidden from the user.

Implementation details such as these nodes can use public member variables without fear -- the reasons for keeping them private are no longer valid.

It is occasionally the case where it is sound to have "exposed" public members. An example is the std::pair class template. The pair is just a poor man's tuple. It implies no policy, it's purely a means of pairing two types for ease of manipulation. This use is certainly rare -- I can't think of any reason to do so other than tuples -- but should not be ignored. Languages with built-in tuple support (found in most or all functional languages, including the C-like Vault) make the components of the tuple public -- it's the right decision.

Log in or register to write something here or to contact authors.