Sign in to follow this  
random_thinker

C++ are classes overused and overrated?

Recommended Posts

Classes can be useful, but let's face it, when a class is implemented it really represents a new type, and how often does a software programme really need a new type? Far better to use the existing STL classes/types and namespaced functions, function templates, and rarely, new classes/types. OK, where maintenance of state in a function is important, then functor-type classes are useful, but these are not really types, they are a more powerful version of a functional programming approach. And where function pointers were used in C, abstract base classes can be used, but again these are not really new types, which is the idea behind the introduction of C++. Class templates represent a powerful extension of classes, but again, these are often non-intuitive and awkward to use until they can be wrapped in a function template that is implicit in its use. Again, we're back to the functional paradigm. Many beginner C++ programmers have a tendency to go overboard with classes and inheritance, where, in the majority of cases, composition is more suitable. A programme with too many types, though, is a monster of a thing to manage and extend, no?

Share this post


Link to post
Share on other sites
For example, in my current work, I only have a few new types; and these tend to be class templates, for example 'math::numeric_sequence<>' and 'math::generated_numeric_sequence<>' which: (1) make the application of std::vector<>, std::list<>, and std::deque<> easier, unify their common methods and offer convenience methods for operations on their data. But instead of template specialisation, the methods refer to overloaded function templates that generically use STL algorithms and iterators. From my experience this approach reduces the amount of code needed, encourages code reuse and offers a more multi-purpose application (the function templates are available for many other uses).

Share this post


Link to post
Share on other sites
WOAH. wait. what?
You make it sound like you'd rather write "void Normalize(Vec3 *)" than "Vec3::Normalize()", which is fine, but not an argument against classes.
Besides you keep saying "class/types", well what would you call just about any data in a program?
a type? not a type? Because any game/application is likely going to need TONES of data types for all the data.
So, the question becomes just a sense of style. Do we want functions that operate on our data? or data that is in a class with operations? or a little of both?
Guess I'm just having a hard time seeing what you are bashing.

Inheritance can be used poorly, and I would argue many people don't immediately understand the proper use of it, but
it defiantly makes a program easier to extend than not having it. I honestly couldn't conceive of how to rework
my state-machine to not have states as classes.

A side bash at STL-ism being that it is good for the general case, but not the specific. The EA-STL papers go
over some of the game related issues of using out-of-the-box implementations of STL. And I've read other places
that some console compilers are less robust compared to high end workstation/server compilers
so templates and inlined functions can throw them for a loop as far as getting good optimization.

And then just a personal note, what is easier to work with under the assumption of an intelesense IDE:
Functional approaches where you have to remember "GameUtil? nope was it is MathUtil? nope GraphicsUtil? ok that is was it GraphicsUtil_TransformVecToScreenSpace()"
or a class approach, where you can just to "myVec.(intelesense list) what do i want, TransformVecToScreenSpace()"
(and let me guess, you are going to respond with why is is named 'GraphicsUtil_' well, in a functional regiem, convention would
dictate you prefix functions to avoid conflicts like Update() having to be cat_update(), dog_update(), etc.
and you do NOT break coding conventions in a large group unless you have a really really good reason to because
it causes even more confusion.)

[Edited by - KulSeran on August 15, 2007 3:26:20 AM]

Share this post


Link to post
Share on other sites
I disagree. You can do things functionally, yes. But then how do you handle member variables? You either pass a struct to the function - in which case you're just emulating classes the C way. You could use globals, but that's horrible and won't let you have two instances of the data.

One perfect example is the "Player" class we use in our game. There's a base player class which handles the common functionality for all players, like following waypoints, collision and so on, and then derived classes like AI players, the "real" player and network players which handle extended functionality.

You could still do that with functions, but it'd be absolutely horrible to do.

Share this post


Link to post
Share on other sites
Quote:
Original post by random_thinker
Many beginner C++ programmers have a tendency to go overboard with classes and inheritance, where, in the majority of cases, composition is more suitable. A programme with too many types, though, is a monster of a thing to manage and extend, no?
I think that depends on whether you come from learning C first (or perhaps even another non-OO language), or whether you started with C++. The former case, IMHO causes people to get stuck in the ways of non-OO. Whereas in the later people could admittedly overuse classes and inheritance. Classes with nothing but getters and setters can be another side-effect of starting out directly with C++.

I don't think classes are overused in general, just maybe by a few rookies[smile]. Classes/structs are essential for RAII, and without them writing correct code is very difficult and error-prone.

PS: Your username is very fitting!

Share this post


Link to post
Share on other sites
Quote:
and how often does a software programme really need a new type?

When some new functionality is needed.

Quote:
Far better to use the existing STL classes/types and namespaced functions, function templates, and rarely, new classes/types.

The exact same argument applies to every language construct. Why use functions when you have std::copy, std::find, etc. Why have global variables when you have std::cout? Why have type aliases when you can use std::string? You have all these to implement other things with them. If you want an XML library you need new types and if you want file system functionality you need new types.

Quote:
OK, where maintenance of state in a function is important, then functor-type classes are useful, but these are not really types, they are a more powerful version of a functional programming approach.

A functional approach with state? Most functional code should be referentially transparent.

Quote:
And where function pointers were used in C, abstract base classes can be used, but again these are not really new types, which is the idea behind the introduction of C++.

I'm not sure I follow, how can an ABC be used as a function pointer and how are ABCs used without introducing new types?

Quote:
Class templates represent a powerful extension of classes, but again, these are often non-intuitive and awkward to use until they can be wrapped in a function template that is implicit in its use.

Why are they awkward to use? IMO templates is a very intuitive concept once you're familiar with it.

Quote:
Again, we're back to the functional paradigm.

How does that lead us to functional programming.

Quote:
Many beginner C++ programmers have a tendency to go overboard with classes and inheritance, where, in the majority of cases, composition is more suitable. A programme with too many types, though, is a monster of a thing to manage and extend, no?

No. A decent OO program makes sure to reduce coupling to a minimum so it doesn't become harder to manage or extend. A lot of small types with their own distinct responsibility will allow you to maximize extensibility. Together with some policy-based programming you can attain extensibility and manageability much larger than what can easily be done in a procedural or functional programming.

Quote:
I usually do it functionally until I need to do it OO

Functionally? Sure you don't mean procedurally?

Surely there is nothing wrong with procedural or functional programming, but neither is there with OO programming.

Share this post


Link to post
Share on other sites
What I'm thinking, guys, is that the early ideas of C++ and other OO languages was that hierarchies of class types were the way to go. But hierarchies are, by nature, rigid structures, no? However, blending OO with a more functional approach leads to an organic structure, that can change/be changed much more easily. As for templates, function templates offer the ability to wrap class templates very very conveniently.

I agree that the class/inheritance model does have its place, but use them too much and you'll soon find that, as your programme grows, you will have to make a fundamental re-write of the code. Watch out for those rigid hierarchies.

KulSeran...I would like to be able to write both "void Normalize(Vec3 *)" and "Vec3::Normalize()". That is the way to design, when it is needed, as in my previous example. It really does not take more code when using templates, because the need for specialisation of classes can be reduced. That way one can, in future, re-use code extensively.

Evil Steve...As for member variables, give a small example, let's see what disadvantage there is...

Share this post


Link to post
Share on other sites
Quote:
Original post by random_thinker
What I'm thinking, guys, is that the early ideas of C++ and other OO languages was that hierarchies of class types were the way to go. But hierarchies are, by nature, rigid structures, no?
True, and this inheritance-heavy approach to design rapidly creates a large mess. People who know what they're doing tend to avoid these deep types of hierarchies.
Quote:
However, blending OO with a more functional approach leads to an organic structure, that can change/be changed much more easily. As for templates, function templates offer the ability to wrap class templates very very conveniently.
I don't know what you call conveniently, but compared to proper functional languages, expressing any sort of functional construct in C++ is a horrible mess. The template and macro voodoo doesn't make it nice, it just makes it possible. It's an unfortunate reality we have to deal with in writing C++ code, though.

Share this post


Link to post
Share on other sites
Quote:
Original post by random_thinker
but these are not really types, they are a more powerful version of a functional programming approach. [...]

Class templates represent a powerful extension of classes, but again, these are often non-intuitive and awkward to use until they can be wrapped in a function template that is implicit in its use. Again, we're back to the functional paradigm.


procedural != functional. Your examples/arguments seem to be in favor of a procedural style of programming. Functional programming is something else entirely. Of course you may know this already and I merely misread your post.

Anyway, one must of course, you must use the right tool for the job. If some task maps better on to a sequence of sub-tasks, a procedural approach will yield the most readable solution. If instead you need to represent a soup of objects and the communications between them, OO is your man. If you're writing algorithms that are highly recursive in nature with no or little shared state, a functional style may suit (though non-trivial to emulate in C++ without using some frankly nutty frameworks). If you need to work with types and manipulate data at compile time, generics can be used.

The real power comes when you mix and match the techniques and styles appropriately. For this reason I do agree that the whole "make everything an object" philosophy is a little silly.

Edd

Share this post


Link to post
Share on other sites
Promit...what I mean by 'conveniently' is that in most cases, the user of a class template must know the type to specify, the precise order and the syntax. When this is wrapped by a function template where the template parameters are implicitly obtained from function arguments, the user does not need to know anything about the template parameters and syntax.

I suppose that my philosophy is to use as little code as possible and to make the code as reusable as possible. To do this, often the OO paradigm is not the best one, and a blend of techniques, sometimes leaning more heavily on a functional one, is better. Don't get me wrong, though, I'm not saying that OO should never be used, I'm saying that it is often overused.

Share this post


Link to post
Share on other sites
My argument against you "rigid hierarchy" point is that well, it just isn't the case in anything i've seen.
For one, you can multiply inherit, so missing functionality at one level can be seamlessly brought in on the level it is needed.
For another, a change in the base of the hierarchy of any design is going to cause a rewrite of SOMETHING, but at least with a
class hierarchy, a change to a base interface can be seamless through the use of virtual functions, and as such would not require
any rewriting except for the classes that use the new interface functions.

Quote:

KulSeran...I would like to be able to write both "void Normalize(Vec3 *)" and "Vec3::Normalize()". That is the way to design, when it is needed, as in my previous example. It really does not take more code when using templates, because the need for specialisation of classes can be reduced. That way one can, in future, re-use code extensively.


1) Code conventions. You can't mix interfaces to something, it confuses the other programmers. Pick an interface, document it
and pass it along to everyone else. I don't want to be searching 2 places for the function I need if i don't have to.
Different objects can have different interfaces, but the same one can't. You can have "Monster::Update()" and "Vector_Normalize ( Vec *)"
but don't mix interfaces on one item, like my quoted example.

2) Specilization is KEY to getting performance out of a piece of code. I'm not trusting a template to get it to work right.
And if i'm going to specialize for every case that a template would be instantiated, why not just drop the template so it is easier to debug.
(side note: templates are nice when i 'just need it to work', and they do have a place. I'm just playing devil's advocate and pointing out there are lots of places where a template doesn't work, or isn't the right tool.)

3) I reiterate from above. Easier to debug without templates.

Quote:

Evil Steve...As for member variables, give a small example, let's see what disadvantage there is...


Just cauze i dont write code the way you are thinking, I'd like to know a way to write something like:
class Object
{
public:
virtual Object *GetOwner() { return NULL; }
};
without the inheritance hierchy. Cauze a lot of the issue with code i write at work is that you HAVE to leave out
everything you don't need (memory at a premium). But things still need to be compatible with specific interfaces.
And yes, I know one solution is to make your own 'VirtualFunctionTable' to use, but what happens when you need to change the 'VFT', lots of rewriting.

Share this post


Link to post
Share on other sites
Edd...I don't support procedural programing at all, and I hope that my comments haven't indicated so. It's just not my style. I've tried to re-use and extend procedural Fortran (it is used widely in numerical analysis work here), and I'd rather have all my teeth pulled out first. Talk about spaghetti code!

Share this post


Link to post
Share on other sites
[ sarcasm]
AfroFire, because with assembly you might actually get performance out of your inner loops.
And who in their right mind would want that.
[ /sarcasm]

<--- Forum needs [ sarcasm] tag.

But yeah, I'm a huge fan of OO programming, but I also learned that straight OO is NOT always the way to go.
It is easy to get overwhelmed with strict OO, and make all-encompassing interfaces, lots of accessor functions,
bloated hierchies of Object->PhysicsObject->ThinkingPhysicsObject->Monster->Cat->FuzzyCat->CuteFuzzyCat->CuteFuzzyCatWithAttack
and all the like. But classes have a place. OO has a place.
And OO can be done without classes, but it isn't always directly equivalent or as simple.

-- edit cauze i don't feel like posting again.
I'm mostly using random examples i can think of to counterpoint you.
I'm not implying that anything I do has tones of A or tones of B. CPU time and
RAM usage are at total premiums at work
so anything that adversly affects one or the other is refactored to be more balanced between the two.
But readability, refactorability, and debugability are also premium.
So templates are often a no-no.
Operator overloads, OO breaking implementation workarounds (i know struct X has a member ->next as the first 4bytes, so I'm just going to .... ), are often no-no's aswell.

[Edited by - KulSeran on August 15, 2007 5:33:20 AM]

Share this post


Link to post
Share on other sites
KulSeran..I'm not saying that OO should never be used, I saying it is overused. Your approach uses a lot of vtables, especially multiply inherited hierarchies of objects and these affect memory consumption and can affect performance, no? As to the interface, expose your users to the interface that they will need, but have the flexibility to recombine or recompose the base framework in future. Agreed that templates can also be an inappropriate option, but I use them extensively for framework-type programming. Look, I don't want to go back to C and I don't want to use Fortran, I want to use C++, mainly because it is so flexible and has the STL, and it also is at the level that I want to work at (as opposed to Assembly...).

Share this post


Link to post
Share on other sites
You know, why have things occupying resources longer than they need too, with functions, and function templates that wrap class template objects, when you need them, you use them, and then their contents go out of scope. Nice an clean. But with objects, they persist, are they really needed? Then the programmer has to think about the persistence and resource consumption of all these objects, no?

Share this post


Link to post
Share on other sites
Quote:
Original post by random_thinker
But with objects, they persist, are they really needed? Then the programmer has to think about the persistence and resource consumption of all these objects, no?


Quick summary of what you just said:

Quote:
When you use objects, and are done with using them, they go out of scope and release memory. But what about when you use objects?


There is nothing which forces objects to persist: if you don't need an object anymore, you either destroy it manually (which is so very not C++) or have it go out of scope. And, unless you use only plain old C++ function pointers, you will be using objects somewhere in your code (especially if you're using the standard library, which relies on several such concepts).

Share this post


Link to post
Share on other sites
It can be argued that it is a 'good thing' to get programmers to think about persistence of objects; but then in reality, the management and destruction order becomes crucial and can be complex in C++.

Coming back to the issue of multiple interfaces; for example, I have a class template with a method that refers to a function template. The class is built around, say std::vector<>, because this container and data are needed for persistence purposes. But I also want the option to feed a stream of (non-containerized) numbers to something that can give me a result. In this case, the function can achieve this (where no memory is needed for a container), but the container-based class that uses this function is optionally available for persistence of data. By writing the template function to process the data and a container class that uses the template function, I can achieve both with less code. Additionally, I have a function template that I can integrate with the STL algorithms as well, should I need to in future. Voila! Code reuse and flexibility!

Share this post


Link to post
Share on other sites
Quote:
Original post by random_thinker
By writing the template function to process the data and a container class that uses the template function, I can achieve both with less code. Additionally, I have a function template that I can integrate with the STL algorithms as well, should I need to in future. Voila! Code reuse and flexibility!


If your point is that generic programming can be useful and result in code reuse and flexibity in situations where it is adapted, then the conclusion is made pretty obvious by the existence of entire libraries based on generic programming (large portions of both the SC++L and boost, for starters).

However, I was under the impression that this thread originally aimed at explaining not "Generic programming is sometimes good to use.", but the quite different point that "Objects and classes in C++ are inferior to using generic programming."

You have missed two important details about this: first, you have not explicitly defined what you refer to as "classes". If one is to assume that it's any usage of the class (and, of course, struct) keyword in C++, then you simply cannot be right—what generic programing can achieve without using those keywords is restricted to a few silly tricks. If, on the other hand, you are referring to a specific usage of those keywords, then you should start by describing what that usage is.

The second, and much more important point, is about what code reuse really is. The definition "being able to reuse code without rewriting significant portions" is woefully lacking: who should be able to? It is essential for code to be reusable that "a programmer skilled in the language but unfamiliar with the system" should be able to reuse the code without either rewriting significant portions, or having to spend hours understanding how the code interacts with the rest of the system.

I agree with you that a function, either template or non-template, is easily reusable in this respect, because its operation can be understood in terms of its arguments and return values only. However, functions represent actions. A function can reliably and understandably represent the action of sorting, the action of printing something on the screen or the action of counting the elements with a property. You could certainly use a function (or a functor, if state is needed) to represent a socket, a car or a bank account, but to the human mind these things are not actions, but objects (which may perform or be the target of actions). No serious general-purpose programming system can be complete without a way to represent both actions and objects.

C++ is no exception—you can only go so far without using new types. You can use a pair of reals to represent a point, a vector of pairs of reals to represent a polygon, and generally rely on standard library types to represent objects. But most objects cannot be sanely represented this way. For instance, a supermarket item (for a cashier program), a book in a library, a student in a school, a racing car in a video game and all such similar entities do not translate well in terms of standard library constructs—they have their own set of possible states which need to be represented in one way or another, and creating a brand new class to represent them offers a flexibility not available by simply combining existing objects.

Share this post


Link to post
Share on other sites
Modern software development is almost entirely OO. Look at Java / .net - you don't have the choice like in C++. OO stuff is much easier to design because of classes being something you can draw, interacting with each other.

And when you use interfaces properly, you don't lose any kind of flexibility.

If you find C++ hard to do things the OO way, it just means you are still thinking procedurally rather than OO. It is not just a case of adding classes to your C project, it's a whole different way of thinking you have to learn. Just like trying to program Haskell in a procedural way is no good - you have to understand the functional programming paradigm to use it. The same is true of OO.

Share this post


Link to post
Share on other sites
It could be argued that OO programming takes a 'top-down' approach, whereas functional programming lends itself to a 'bottom-up' approach. Is there somewhere in the middle, or does one just end up caught in a sandwich, being taken to lunch?

Share this post


Link to post
Share on other sites
Quote:
Original post by random_thinker
It could be argued that OO programming takes a 'top-down' approach, whereas functional programming lends itself to a 'bottom-up' approach. Is there somewhere in the middle, or does one just end up caught in a sandwich, being taken to lunch?


Test driven development can drive your code either direction; from the bottom up or top down.

WRT the earlier point-- I'd have to echo the earlier sentiments that yes, classes (especially inheritance) can be overused... by rookies.

Classes are a way to provide an interface for a set of functionality, exposed through polymorphism. Polymorphism is a good thing™. It's not the only way to do things; but it's a good tool.­­

I don't believe that the argument against templates (awkward/non-intuitive) holds true for those people who know what they're doing...nor that a template class should have to be "wrapped" in any sort of way.

And I don't hear ANYBODY suggesting that monolithic heirarchies are the way to go [smile]

So what this boils down to is... preference for one set of tools over another.

Share this post


Link to post
Share on other sites
Verg...I find templates very very useful, both class and function. For a functionally oriented programmer, function templates can be a dream come true. But class templates, when applied, generally require explicit types (unless default parameters are used), whereas function templates can often be made without; the user would not even know they are using a template.

I suppose this comes down to what Tooh... was saying, this is Generic programming, which I really prefer, regardless of OO or function approaches.


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