Introduction
This article explains why C++ (among other Object Oriented Languages) was invented. What its advantages over procedural languages are, and it shows an example of this.
What is Object Orientation (OO)?
Once upon a time...
In a simple line: OO is a big improvement in the organization of code. Object Oriented Programming (OOP) is kind of a philosophy. Let me explain: suppose there is a company, a BIG company. And every employee in this company has access to everything (is it still a company?). Even the cleaner has access to the science department ready to mess around with some dangerous toxins. Luckily, everyone is very faithful and will not do such a thing someday soon. Then, one day, after this company grows even bigger and more complex, the whole organization collapses. Quickly now, what's the problem here? Exactly, the organization has become too complicated. Everything is confusing, the scientist couldn't find the toxins anymore because the cleaner messed around with them. The salesmen went crazy because the telephonist accidentally sold everything, etc.
Now, think of this company as a program. This program has no real structure. Everything knows everything, this is not much of a problem in a simple Hello World program. But like I said, this is a BIG program (company). The cleaner, scientist, salesmen, and the telephonist in this program are functions. And the toxins and the products for sale are the data. There is of course much more, use your imagination. The point here is that every function has access to all data, which is the case in procedural languages like C (although C has some features to prevent this, they aren't much I guess). Meaning that, in a situation, data could easily be lost or corrupted when a new function is made. And if a data item needed to be edited, you would also have to edit every function which works with that particular data item... This is not very fun when you have worked on a program for like let's say... a few months.
OO to the rescue!
This is why OO is here, to make irrational programs like the one above more rational. To make the chaotic ordered etc. Let's use our imagination again... suppose there is a company, a BIG company. This company is very successful and is divided into multiple departments. Each department has its own purpose and its own security measures. This time, the telephonist cannot sell any products because all the products are located in a storage facility to which only the salesmen have access (HAH!). And the cleaner doesn't have access to the science department either, because only the scientists have the key to it! This, can be called a company. There is a rational organization with security measures to prevent any corruption or sloppiness within the company. Now, the boss (yes, this time there is a boss) can go home relaxed without having to worry about the wild fantasies of the cleaner. One word: OOP. We could do this to the program by using classes. This is what we're gonna do.
The Company example
OOP-less
Let's take a look at what disaster could occur in a program without OO. (The example is small and unreal, but it will help you get the point). Note: it is still written in C++ but without the OO concept in mind.
#include <iostream.h>
#include <conio.h> //for getche
const int LastChecked = 10;
int toxin = LastChecked;
int products = LastChecked;
void cleaner()
{
cout << "\nHAHAH I AM AN EVIL CLEANER!!\ntoxin = " << toxin
<< "\n*Alters toxin!*\n";
toxin += 2;
cout << "toxin = " << toxin << endl;
}
void telephonist()
{
cout << "\nHAHAH I AM AN EVIL TELEPHONIST!!\nproducts = " << products
<< "\n*Sells all products!*\n";
products -= 10;
cout << "products = " << products << endl;
}
void scientist()
{
cout << "\nScientist:\n";
if(toxin == LastChecked)
cout << "I'm glad nobody messed with my toxin!\n";
else
cout << "Oh my god, somebody messed with my toxin!!!!\n";
}
void salesman()
{
cout << "\nSalesman:\n";
if(products == LastChecked)
cout << "I'm glad nobody sold anything!\n";
else
cout << "Oh my god, somebody sold stuff!!!!!\n";
}
void main()
{
scientist();
salesman();
cleaner();
telephonist();
scientist();
salesman();
cout << "\n\nPress enter."; cout.flush();
getche();
}
As you can see here, toxin
and products
are defined globally. Meaning they are accessible by any part of the program. So now, cleaner()
and telephonist()
can both access these variables. Which is of course what we don't want. We only want scientist()
and salesman()
to be able to access these variables. Then again, we don't want scientist()
to be able to mess with products
and the same goes for salesman()
and toxin
.
There is another problem with the above code. The program doesn't really fit in real life situations. Take scientist()
, it is a function. Now in real life, a scientist would be more of an object instead of a function. And the scientist checking up on the toxins would be the function of the scientist. So how does OO solve all this? In C++, it solves this by using classes. I will not go very in-depth on classes in this article (if you do want in-depth info on classes, go here).
OOP-ness
Now, let's see how OOP solves the problem using classes in this example:
#include <iostream.h>
class Scientist
{
private:
int toxin;
public:
Scientist(int Toxin_Value) : toxin(Toxin_Value)
{}
int GetToxin()
{
return toxin;
}
void AlterToxin(int Toxin_Value)
{
toxin = Toxin_Value;
}
};
class Cleaner
{
public:
void Evil_Altertoxin(Scientist& victim, int value)
{
victim.toxin = value;
}
};
void main()
{
const int DaveVar = 10;
Scientist Dave(DaveVar);
Cleaner John;
cout << "\nScientist Dave:\n";
if(Dave.GetToxin() == DaveVar)
cout << "I'm glad nobody messed with my toxin!\n";
else
cout << "Oh my god, somebody altered my toxin!\n";
cout << "\nEVIL Cleaner John:\nLet's try to alter Dave's toxin!";
John.Evil_Altertoxin(Dave, 0);
toxin = 0;
}
Good_Workers.cpp will generate 2 compiler errors. The first one for this:
victim.toxin = value; on line 32.
And another one, for this:
toxin = 0; on line 54.
First of all, let's look at our class. The Scientist
class, has one data item, two member functions and a constructor. The member functions are defined under the public
scope resolution operator. When something is defined under the public
scope resolution operator, the whole program has access to the data, however, whatever data or member functions are under it, they are still not globally defined. This is why the statement toxin = 0;
generates a compiler error, because the compiler can't see a global toxin. And it would say something like:
error C2065: 'toxin' : undeclared identifier
But then, how can the members of a class be accessed, you ask? By the dot operator, as you can see with Dave.GetToxin()
and John.Evil_Altertoxin(Dave, 0)
. Speaking of which, let's take a look at that member function Evil_Altertoxin()
:
Encapsulation
void Evil_Altertoxin(Scientist& victim, int value)
{
victim.toxin = value;
}
This particular function takes an argument of type (class) Scientist
, and one of type int
. Then it tries to alter victim
's toxin
by assigning it with value
. Now everything looks okay, since we formally used the dot operator to access victim
's toxin
. However, the compiler gives us an error that says we still can't access it! This is because we have declared toxin
under the private
access specifier. When a private
access specifier is stated somewhere in the class, it means that everything following the access specifier can only be accessed by other parts within the class itself (until another access specifier is stated). In the Scientist
class, this means that only the functions GetToxin()
and AlterToxin()
(and the constructor) can access toxin
. This also means that a function like Evil_Altertoxin()
CAN'T access toxin
because it is not a member of the Scientist
class (this is called encapsulation).
Just be nice!
There is, however, one way for John
to "access" toxin
. He could ask Dave
to alter toxin
to a desired value. Then instead of:
victim.toxin = value;
We would have to state:
victim.AlterToxin(value);
Even then, John
doesn't really access toxin
, because Dave
still does all the work here.
New datatypes!
const int DaveVar = 10;
Scientist Dave(DaveVar);
Cleaner John;
Have you also noticed the declaration of Dave
and John
? Yes people, we have created 2 new datatypes: Scientist
and Cleaner
! Now, instead of functions, they are actually instance data, which is a lot more comparable to the real world. Everything fits together as one!
"Thank God, it's just 1!"
What about the I-don't-have-to-rewrite-every-function thing? Suppose you have a function that returns the square number of an argument, and you want that function to be able to operate on as many datatypes as possible. With or without overloading, you would still end up writing a lot of functions. Thanks to a new feature, templates, we only have to write one!
#include <iostream.h>
#include <conio.h> //for getche
template <class T>
T SqrNum(T argNum)
{
return (argNum * argNum);
}
void main()
{
int inum = 2;
long lnum = 4;
double dnum = 5.6;
cout << "inum * inum = " << SqrNum(inum) << endl
<< "lnum * lnum = " << SqrNum(lnum) << endl
<< "dnum * dnum = " << SqrNum(dnum) << endl << endl
<< "Press enter."; cout.flush();
getche();
}
What's happening here!? I'll explain it to you, however, I won't go in-depth on the syntax. The function SqrNum()
isn't actually being created on definition. It is more of a blueprint for creating other functions. When the compiler receives a "call" to a template function, it won't actually call the template function. Instead, the compiler will create the right function for the right datatypes using the template function. In the first "call" to SqrNum(inum)
, the compiler will start a process like this:
- Hm...
inum
is of type int
, so we'll treat T
as int
.
- Hm... let's create a function that works on our
int
argument.
(it will look like this:)
int SqrNum(int argNum)
{
return (argNum * argNum);
}
And when that is done, the actual function call is being made. Of course, the same goes for SqrNum(lnum)
and SqrNum(dnum)
.
Also note: the compiler won't actually replace the template in the source file with the functions!
Infinity...
Now, there are loads of more stuff that you can do with classes, by classes, or in classes. Like, operator-overloading, function-overloading, inheritance and polymophism, abstract classes and even more. It's just beyond the scope of this article. OO is what you do with OO, and beyond... ;-)
Any problems with this article? Let your hart out! I'm writing this article as much as to share stuff, as to learn stuff. (I also noticed that I am explaining this article as if OO is just encapsulation, please don't take it that way.)
History
- April 7th 2004: Article posted.