Skip to content

June 7, 2012

Quick Guide to Object Oriented Programming in C++ (Part 1)

by noise

This is a quick guide to Object Oriented Programming in C++ (aka OOP). Which even if not exhaustive, we want it to be a very practical guide to Object Oriented Programming in C++.

1. Defining a Class in C++ (Example 1)

Starting to learn programming in C++ let’s see some examples. Next is a simple example how to define a C++ Class. There are three files: the header file for the class, the implementation file for the class and main program that make use of the class.

#ifndef SCORE_H
#define SCORE_H
// Score.h --- header file for Score class
 
#include <string>
 
using namespace std;
 
class Score {
    public:
        // class constructor
        Score();
 
        // class destructor
        ~Score();
 
        void SetPlayerName(string playerName);
        string GetPlayerName();
 
        void SetPlayerId(int plId);
        int GetPlayerId();
 
        void SetScore(int sc);
        int GetScore();
 
    private:
        string playerName;
        int playerId;
        int score;
};
 
#endif

By looking at the header file with definition of the class (Score.h) we notice we’ve defined variables (also called class variables or properties in some programming languages) and function members (also called methods in some languages like Java).

We also notice two sections defined: public and private. We’ll talk about that later, for now just remember that class variables and function members defined in public section can be accessed from outside, but class variables and function members defined in private section can be accessed only by class function members.

Now the implementation file (Score.cc):

// Score.cc --- implementation file for Score class
#include "Score.h"
 
Score::Score() {
    playerName = "";
    playerId = 1;
    score = 0;
}
 
Score::~Score() {
 
}
 
void Score::SetPlayerName(string plName) {
    playerName = plName;
}
 
string Score::GetPlayerName() {
    return playerName;
}
 
 
void Score::SetPlayerId(int plId) {
    playerId = plId;
}
 
int Score::GetPlayerId() {
    return playerId;
}
 
void Score::SetScore(int sc) {
    score = sc;
}
 
int Score::GetScore() {
    return score;
}

And the main program is (main.cc):

#include <string>
#include <iostream>
 
#include "Score.h"
 
using namespace std;
 
int main(void) {
    Score playerOne, playerTwo;
 
    playerOne.SetPlayerName("John");
    playerOne.SetPlayerId(1);
    playerOne.SetScore(100);
 
    playerTwo.SetPlayerName("Mary");
    playerTwo.SetPlayerId(2);
    playerTwo.SetScore(200);
 
    cout << "PlayerOne Name: " << playerOne.GetPlayerName() << endl;    
    cout << "PlayerOne ID: " << playerOne.GetPlayerId() << endl;    
    cout << "PlayerOne Score: " << playerOne.GetScore() << endl << endl;
 
    cout << "PlayerTwo Name: " << playerTwo.GetPlayerName() << endl;    
    cout << "PlayerTwo ID: " << playerTwo.GetPlayerId() << endl;    
    cout << "PlayerTwo Score: " << playerTwo.GetScore() << endl;    
 
    return 0;
}

To compile the code (using gcc or clang) run:

g++ -lstdc++ Score.cc main.cc -o mainapp
or:
clang -lstdc++ Score.cc main.cc -o mainapp

or:
gcc -W -Wall -ansi -pedantic -O2 -lstdc++ Score.cc main.cc -o mainapp

Then you will run: ./mainapp.

This is the simplest example of using classes in C Plus Plus and working with objects. We will see in next section what are the advantages of using Object Oriented Programming.

2. Make Use of The Destructor (Example 2)

We’ve used string type in our first example which make things easier because we do not have to allocate memory. Next example will use char * instead of string. We also removed PlayerId functions to simplify the code.

So for our second example our class header definition file (Score.h) is:

// Score.h, our header file for Score class
 
#ifndef SCORE_H
#define SCORE_H
 
#include <string>
 
using namespace std;
 
class Score {
    public:
        // class constructor
        Score();
 
        // class destructor
        ~Score();
 
        void SetPlayerName(char * playerName);
        string GetPlayerName();
 
        void SetScore(int sc);
        int GetScore();
 
    private:
        char * playerName;
        char * playerName2;
        int score;
};
#endif

Then our class implementation (Score.cc) is:

// Score.cc, our implementation file for Score class
#include <iostream>
#include "Score.h"
 
using namespace std;
 
Score::Score() {
    playerName = NULL;
    score = 0;
}
 
Score::~Score() {
    if ( playerName != NULL) {
        delete playerName;
    }
}
 
void Score::SetPlayerName(char * plName) {
    int size = sizeof(plName);
 
    playerName = new char[size];
    if ( playerName == NULL ) {
        cout << "Memory Allocation Error" << endl;
        exit(1);
    }
 
    sprintf (playerName, "%s", plName);
}
 
string Score::GetPlayerName() {
    return playerName;
}
 
void Score::SetScore(int sc) {
    score = sc;
}
 
int Score::GetScore() {
    return score;
}

And finally our main app (main.cc) is:

#include <string>
#include <iostream>
 
#include "Score.h"
 
using namespace std;
 
int main(void) {
    Score playerOne, playerTwo;
 
    playerOne.SetPlayerName((char *)"John");
    playerOne.SetScore(100);
 
    playerTwo.SetPlayerName((char *)"Mary");
    playerTwo.SetScore(200);
 
    cout << "PlayerOne Name: " << playerOne.GetPlayerName() << endl;    
    cout << "PlayerOne Score: " << playerOne.GetScore() << endl << endl;
 
    cout << "PlayerTwo Name: " << playerTwo.GetPlayerName() << endl;    
    cout << "PlayerTwo Score: " << playerTwo.GetScore() << endl;    
 
    return 0;
}

Now just compile the code using:
gcc -W -Wall -ansi -pedantic -O2 -lstdc++ Score.cc main.cc -o mainapp

You will notice the delete function from our destructor which dealocate memory we’ve allocated in our setter (SetPlayerName()).

Also you will notice later that there’s a mistake (wrong approach) in this example. In Example 3 we’ll solve that and will explain the issue.

3. Debugging The Code

First there’s GDB, which we will mainly use to debug our code. If our program crashes in order to debug it we will compile it using -g parameter:

gcc -g -W -Wall -ansi -pedantic -O2 -lstdc++ Score.cc main.cc -o mainapp

Then we will run the program with GDB:

gdb mainapp
or:
gdb mainapp mainapp.core

When being in gdb we will type run command:

(gdb) run

Then after the program crashes to see the line where it crashed we run:

(gdb) up

Then to see the value of a variable we are interested in and memory location we run:

(gdb) print var_name;

Even if we were carefully when allocating memory is good to know how to debug our code for performance and for memory leaks. There are some useful tools just for that purpose, some of them being Electric Fence and Valgrind. There’s also DTrace that we will use for this purpose. DTrace is a very powerful tool and for now is available only on Solaris, OSX and FreeBSD systemes. Even if you compile for other platforms if you use GCC or CLang compiler you could audit your app for memory leaks and performance on one of therese OSes.

3.1 Debuging memory leaks (using Dtrace)

– work in progress!

3.2 Performance analysis

– work in progress

4. Constructor With Parameters (Example 3)

– work in progress

If you notice in Example 2 there’s no memory allocation for our class variables in constructor. We deal with memory allocation in our setter function (SetPlayerName()) because just then we know the size of playerName variable.

We could allocate a memory in our constructor and asign a default value (for example the string “PlayerOne”. And we could also use a constructor with parameters to asign our values right when we define our class objects.

So let’s do that.

So our header file (Score.h) for Example 3 would be:

#ifndef SCORE_H
#define SCORE_H
// Score.h, header file for Score class
 
#include <string>
 
using namespace std;
 
class Score {
    public:
        // constructor
        Score();
 
        // constructor with parameter
        Score(char * plName, int plScore);
 
        // destructor
        ~Score();
 
        void SetPlayerName(char * playerName);
        string GetPlayerName();
 
        void SetScore(int sc);
        int GetScore();
 
    private:
        char * playerName;
        char * playerName2;
        int score;
};
#endif

Our class implementation file (Score.cc) for Example 3 is:

// Score.cc, implementation file for Score class
#include <iostream>
#include "Score.h"
 
using namespace std;
 
Score::Score() {
    playerName = new char[15];
    if ( playerName == NULL ) {
        cout << "Memory Allocation Error" << endl;
        exit(1);
    }
    sprintf (playerName, "%s", "Default Player");
    score = 0;
}
 
Score::Score(char * plName, int plScore) {
    int size = sizeof(plName);
 
    playerName = new char[size];
    if ( playerName == NULL ) {
        cout << "Memory Allocation Error" << endl;
        exit(1);
    }
    sprintf (playerName, "%s", plName);
    score = plScore;
}
 
Score::~Score() {
    if ( playerName != NULL) {
        delete[] playerName;
    }
}
 
void Score::SetPlayerName(char * plName) {
    int size = sizeof(plName);
 
    playerName = new char[size];
    if ( playerName == NULL ) {
        cout << "Memory Allocation Error" << endl;
        exit(1);
    }
 
    sprintf (playerName, "%s", plName);
}
 
string Score::GetPlayerName() {
    return playerName;
}
 
void Score::SetScore(int sc) {
    score = sc;
}
 
int Score::GetScore() {
    return score;
}

And finally our main program is:

#include <string>
#include <iostream>
 
#include "Score.h"
 
using namespace std;
 
int main(void) {
    Score playerOne, playerThree((char *)"Lazy George", 10);
 
    // before initializing playerOne the value is that defined
    // by our constructor without parameter
    cout << "Default playerOne Is: " << playerOne.GetPlayerName() << endl << endl;    
 
    // we change the value using a setter function
    playerOne.SetPlayerName((char *)"John");
    playerOne.SetScore(150);
 
    cout << "PlayerOne Name: " << playerOne.GetPlayerName() << endl;    
    cout << "PlayerOne Score: " << playerOne.GetScore() << endl << endl;
 
    // values for playerThree are initialize using constructor with parameters
    // right when we define playerThree at the beginning of main program
    cout << "PlayerThree Name: " << playerThree.GetPlayerName() << endl;    
    cout << "PlayerThree Score: " << playerThree.GetScore() << endl;    
 
    return 0;
}

Now if you’ve looked careful to our previous example you will notice memory allocation is done in setter function which is not good because every time we will set the value for playerName we will allocate extra memory without deallocation previously allocated memory. This will lead to memory leaks. So to fix this remove line that allocates memory in our setter function:

playerName = new char[size];

So a variant (variant 2) of our class implementation (Score.cc) would be:

// implementation file for Score class
#include <iostream>
#include "Score.h"
 
using namespace std;
 
Score::Score() {
    playerName = new char[15];
    if ( playerName == NULL ) {
        cout << "Memory Allocation Error" << endl;
        exit(1);
    }
    sprintf (playerName, "%s", "Default Player");
    score = 0;
}
 
Score::Score(char * plName, int plScore) {
    int size = sizeof(plName);
 
    playerName = new char[size];
    if ( playerName == NULL ) {
        cout << "Memory Allocation Error" << endl;
        exit(1);
    }
    sprintf (playerName, "%s", plName);
    score = plScore;
}
 
Score::~Score() {
    if ( playerName != NULL) {
        delete[] playerName;
    }
}
 
void Score::SetPlayerName(char * plName) {
    int size = sizeof(plName);
 
    if ( playerName == NULL ) {
        cout << "Memory Allocation Error" << endl;
        exit(1);
    }
    sprintf (playerName, "%s", plName);
}
 
string Score::GetPlayerName() {
    return playerName;
}
 
void Score::SetScore(int sc) {
    score = sc;
}
 
int Score::GetScore() {
    return score;
}

But this has another limitation. If we set a small value for our parameter constructor (playerThree(“John”,1)) when we will set a new value we will exceed the size of allocated memory.

So to properly solve this issue we either allocate a fixed size for the player name (let-s say 50 characters) or we could deallocate and allocate memory every time we use setter.

Here is new variant (variant 3) of class implementation using deallocation and allocation of memory in our setter function:

// implementation file for Score class
#include <iostream>
#include "Score.h"
 
using namespace std;
 
Score::Score() {
    playerName = new char[15];
    if ( playerName == NULL ) {
        cout << "Memory Allocation Error" << endl;
        exit(1);
    }
    sprintf (playerName, "%s", "Default Player");
    score = 0;
}
 
Score::Score(char * plName, int plScore) {
    int size = sizeof(plName);
 
    playerName = new char[size];
    if ( playerName == NULL ) {
        cout << "Memory Allocation Error" << endl;
        exit(1);
    }
    sprintf (playerName, "%s", plName);
    score = plScore;
}
 
Score::~Score() {
    if ( playerName != NULL) {
        delete playerName;
    }
}
 
void Score::SetPlayerName(char * plName) {
    int size = sizeof(plName)+1;
 
    delete playerName;
    playerName = new char[size];
    if ( playerName == NULL ) {
        cout << "Memory Allocation Error" << endl;
        exit(1);
    }
    sprintf (playerName, "%s", plName);
}
 
string Score::GetPlayerName() {
    return playerName;
}
 
void Score::SetScore(int sc) {
    score = sc;
}
 
int Score::GetScore() {
    return score;
}

5. Object Oriented Programming Concepts

Most important features of object oriented programming in C++ are:
– Encapsulation
– Inheritance
– Polymorphism

Encapsulation is the process of hiding implementation information to the user of the class. To make use of Encapsulation we will make use of public, private and protected identifiers.

To make things simpler let’s see an example: header file from Class implemented in Example 2:

// Score.h, our header file for Score class
#ifndef SCORE_H
#define SCORE_H
#include <string>
 
using namespace std;
 
class Score {
    public:
        // class constructor
        Score();     
        // class destructor
        ~Score();
 
        void SetPlayerName(char * playerName);
        string GetPlayerName();
 
        void SetScore(int sc);
        int GetScore();
 
    private:
        char * playerName;
        char * playerName2;
        int score;
};
#endif

We will notice we make use of Encapsulation by hiding (using private tag) playerName, playerName2 and score variables that are not accessible from outside. Those variables can be accessed only by our class function members (SetPlayerName(), GetPlayerName(), SetScore(). GetScore()) And if you look at Example 2 in main.cc file you will notice we access only public function members.

So what are public, private and protected used for?
public: class variables and function members defined here can be accessed by everybody;
private: class variables and function members defined here can be accessed only by class’s objects;
protected: class variables and function members can be accessed by class’s objects and by objects derived from the parent class.

Our examples (Example 1 and Example 2) make use of Encapsulation feature of OOP.

If you want to know how would you not make use Encapsulation, just define everything as public and then try to access for example class variables from main program instead of using setter and getter functions (functions we used to set a value and get a value from private area of the class).

You may want to know when should Encapsulation be used? Well, Encapsulation should be used in any situation that we can. By using public methods to access private variables we can also control the values assigned to variables from outside. So we will be able to allow only some values. If we let those variables to be accessed directly from outside of the class any values (including invalid values) can be assigned to those variables.

We’ve learned that we cannot access from outside private and protected resources but the idea is that we cannot access them directly. We still can access them through our public function members that on their turn will be allowed to access resources within their class.

This concludes our first tutorial on Object Oriented Programming in C++ and I hope you’ve like it and you will make use of this tutorial.

Read more from C and C++ Tutorials

Leave a Reply

required
required

Note: HTML is allowed. Your email address will never be published.

Subscribe to comments