Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A beginner's guide to Object Orientation

0.00/5 (No votes)
6 Apr 2004 1  
Explains Object Orientation in C++. And a little something about templates.

Sample Image - beginneroop.jpg

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.

//FILE: Evil_Workers.cpp


#include <iostream.h>

#include <conio.h> //for getche


const int LastChecked = 10; //unaltered value


int toxin = LastChecked; //toxin data item

int products = LastChecked; //products in stock


void cleaner()
{
    cout << "\nHAHAH I AM AN EVIL CLEANER!!\ntoxin = " << toxin
        << "\n*Alters toxin!*\n"; //yes he is evil!

    toxin += 2; //cleaner CAN alter toxin!

    cout << "toxin = " << toxin << endl;
}

void telephonist()
{
    cout << "\nHAHAH I AM AN EVIL TELEPHONIST!!\nproducts = " << products
        << "\n*Sells all products!*\n"; //NOOOOO!!

    products -= 10; //telephonist CAN sell products!

    cout << "products = " << products << endl;
}
void scientist()
{
    cout << "\nScientist:\n";
    //scientist checks if toxin is still unaltered

    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";
    //salesman checks if no products are sold

    if(products == LastChecked)
        cout << "I'm glad nobody sold anything!\n";
    else
        cout << "Oh my god, somebody sold stuff!!!!!\n";
}

void main()
{
    scientist(); //scientist checks

    salesman(); //salesman checks

    cleaner(); //cleaner alters

    telephonist(); //telephonist sells

    scientist(); //scientist checks

    salesman(); //salesman checks

    cout << "\n\nPress enter."; cout.flush();
    getche(); //just so the program doesnt terminate immediatly

}

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:

//FILE: Good_Workers.cpp


#include <iostream.h>


//a class named Scientist will be defined here

class Scientist
{
private:    //important!

    int toxin;
public:
    //constructor with initialization list

    Scientist(int Toxin_Value) : toxin(Toxin_Value)
    {}

    //member functions which do something with toxin

    int GetToxin() //return the value of toxin

    {
        return toxin;
    }
    void AlterToxin(int Toxin_Value) //set toxin to Toxin_Value

    {
        toxin = Toxin_Value;
    }
};

//for OO's sake let's create a Cleaner class

class Cleaner
{
public:
    void Evil_Altertoxin(Scientist& victim, int value)
    {
        victim.toxin = value; //this will generate a compiler error

    }
};

void main()
{
    const int DaveVar = 10; //correct toxin value

    Scientist Dave(DaveVar); //create object Dave

    Cleaner John; //create object John


    //Dave checks to see if toxin is still unaltered

    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";

    //John attempts to alter toxin

    cout << "\nEVIL Cleaner John:\nLet's try to alter Dave's toxin!";
    John.Evil_Altertoxin(Dave, 0);

    //just for demonstration

    toxin = 0; //another compiler error

}

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 will generate a compiler error

    }

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; //this will generate a compiler error

We would have to state:

        victim.AlterToxin(value); //this will compile

Even then, John doesn't really access toxin, because Dave still does all the work here.

New datatypes!

    const int DaveVar = 10; //correct toxin value

    Scientist Dave(DaveVar); //create object Dave

    Cleaner John; //create object 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!

//FILE: Templates.cpp


#include <iostream.h>

#include <conio.h> //for getche


template <class T> //warning: translate class as type!

T SqrNum(T argNum)
{
    return (argNum * argNum); //return type is of T

}

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(); //just so the program doesnt terminate immediatly

}

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) //notice how all the Ts have been replaced by ints

{
    return (argNum * argNum); //return type is of int

}

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here