User Tools

Site Tools


handbook:handbook:programin_3

Advanced Concept

In this part we will discuss about advanced programming in C++. This part actually is a reflexion of everything which is valuable in C++, it shows the effeciency and compatibility of the language. We will discuss classes and everything which goes with them, namespaces as a relatively new thing in C++ and other advanced concepts.

Comments

Comments in C++ are changed. Instead of the old C-style comments,using “/*” and “*/”, a new bracket is used. You can use the old comment brackets, but using the new one is recommended. Here comes an example:

/*C-style comment*/

and:

//C++ comment

Few memory keywords

In this short part, we will talk about memory allocation in C++. Memory allocation means to reserve memory for some variable, and place the value in that specific memory address. In C, dynamic memory allocation is done by using the function malloc(). After using, it is freed with free(). However, that is changed in C++. Operators new and delete have the same function as malloc() and free() respectively. But, of course, there are differences. To grasp it better, i will give an example. In order to allocate memory in malloc you would have to do the following:

int* ptr;
  
ptr = (int*) malloc(sizeof(int)); /*allocating here sizeof(int) bytes*/
/* some code*/
free((void*)ptr); /*freeing the memory now*/

But, if we do it in C++:

int* ptr = new int;

//code...

delete ptr;

As you can see, it is far more simply. The advantage in using the new and delete operators is that they take care of the low-level details. You can use new to allocate a whole vector, array, of integers. If we want to allocate 100 integers:

int* ptr = new int[100];
 
//code

delete [] ptr; // or delete ptr;

You can also initialize values with new:

String* Name = new string("Alex");

.....

delete [] Name;

If there is a failure in allocating memory, an exception will be thrown. We will talk later about handling exceptions.

Classes

Classes are the heart of the C++ language. They make the Object Oriented programming reality. Classes have similar functions as ordinary structures, and they are also declared very similary. However, classes can contain functions, fully declared or only prototypes. Anyway, it is recommended to use prototypes because they make the code clearer and easier to understand. Every class contains members. Members in a class can be variables and functions, sometimes called methods. However, to get the best out of the OO programming, every member can be public, private or protected. We will explain these relationships through examples. Let's define a simple class,containing no functions:

class Employee
{
   public:
   int age;
   String address;
};

This is a very simple class. You can easily create object for this class:

Employee object;

and access its members:

object.age = 15;

You can notice that the both variables here are public. That means that they can be accessed directly through the objects of the class, just like we modified the age variable. You can also modify them as:

Employee::age = 15;

However, it is a bad practise to make the variables you work with public. It is highly recommended to modify the variables through appropriate methods, functions in the class. So, we can make a small adjustment:

class Employee
{
  private:
  int age;
  String address; 
 
  public:
  int modify_age(int);
};

//Now we declare the functions

int Employee::modify_age(int new_age)
{
   age = new_age; //we modify the value of the age now.
   
   return age; //and we return the new value;
}

As you can see, the variable age is private now. That means that it not is accessible directly, only through the class methods. However, methods are usually public, because we want to access them with objects:

Employee john;
john.modify_age(45);

We will discuss about protected keyword when we will talk about class inheritance.

Constructors and deconstructors

Objects very often get the need to initialize variables, or to allocate memory for them. To do so, we can use class constructors. Lets assume that we have a class which caluculates a sum:

class Sum
{
  private:
  int sum;
  int counter;
  int limit;
  
  public:
  Sum(); //this is the constructor
  void set_limit(int);
  int build_sum();
};

Sum::Sum()
{
  sum = 0;
  counter = 0;
  limit = 0;
}

void Sum::set_limit(int limit_nr)
{
  limit = limit_nr;
}

int Sum::build_sum()
{
  for (counter = 0; counter < limit; counter++)
  {
    sum = sum + counter;
  }
  
  return sum;
}

So, you can see that in the constructor we initialize the values. Of course this is a very easy example, but you will see that constructors are very useful in larger programs.

Deconstructors are doing the opposite of the constructors. They have the same name as the class, but they have one ~ ahead.For example, if the constructor has allocated memory, the deconstructor will free it:

class Test
{
  private:
  int* int_ptr;
  public:
  Test();
  ~Test();
};

Test::Test() //this is the constructor
{
  int_ptr = new int;
}

Test::~Test() //this is the deconstructor
{
  delete int_ptr;
}

Please note that even if not specified, constructors and deconstructors are automaticcaly built. They always exist, in each class. But, if you re-declare them, you can modify them to your needs, therefore making your code more efficient.

Class inheritance

C++ classes has one very great feature: inheritance. Using inheritance, you can create a very useful scheme for your object-oriented work, you can organize your code better. Class inheritance is useful, when multiple classes have similar, or same, properties. For example, we want to create a animal classes. So, first, we create the top class, the superclass, the generic animal class, and the hierarchy is built as follows:

As you can guess, the generic class, Animal, contain variables which are usefull in every other classes, down to Frog and Snake. This kind of programming gives the opportunity to the programmer to create cleaner and more efficient code. Class inheritance shows the real value of Object-Oriented programming. However, classes can be inherited in several ways.

Public Inheritance

Let's assume that we have two classes. The first:

class Animal
{
  private:
  int legs;
  int eyes;
  
  public:
  void set_legs(int);
  void set_eyes(int);
};

and:

class Mammal : public Animal
{
  private:
  String kind;
  
  public:
  void set_kind(String);
};

The line class Mammal : public Animal tells us that the class Mammal inherits class Animal, but uses public inheritance. What does that mean? Well, it means that all public members in the class Animal, are not public also to class Mammal. That means that functions set_legs(int) and set_eyes(int) can be used freely from class Mammal. Example:

Mammal donkey;
donkey.set_legs(4);
donkey.set_eyes(2);
donkey.set_kind("Four legged");

As you can see, we can use the Animal functions without any restriction.

Private inheritance

When we use private inheritance, we want the public members from the parent class, that is the class which we inherit from, to be private in the child class. In the example above, we could have also used private inheritance:

class Mammal : private Animal
{
  private:
  String kind;
  
  public:
  void set_kind(String);
};

Notice this line: class Mammal : private Animal. You can see that the keyword is changed from public to private, meaning that we use private inheritance now. But now, however, the functions set_eyes(int) and set_legs(int) will be private in the class Mammal, so using the directly through object would be impossible. In this case, using private inheritance is not so wise, but we gave it as example anyway.

Protected type of members

I have said earlier that i would discuss protected type in class inheritance. Please recall that private members are not accessible directly to derived classes in public, nor private, inheritance. Sometimes, however, we get the need to use the private members of the parent classes in the derived ones. We can accomplish that, if they are defined as protected. Let's redefine the class Animal:

class Animal
{
   protected:
   int eyes;
   int legs;

   public:
   set_eyes(int);
   set_legs(int);
};

class Mammal : public Animal
{
   private:
   String kind;

   public:
   void set_kind(String);
};

The protected members of Animal, legs and eyes, are not publicly accessible. However, they are made public only to the derived classes, in this case it is Mammal. In this example, the Mammal objects can use the integers legs and eyes directly, but they are still protected outside that class. To explain it better, we will design the set_kind(String) function:

void Mammal :: set_kind(String kind)
{
  if(compare(kind,"donkey") == 0) //if the kind specified is donkey....
  {
    legs = 4;
    eyes = 2;
  }
}

As you can see, the Mammal function set_kind can freely use the legs and eyes integers, which would not be possible if they were defined as private.

Namespaces

Namespaces are just another way to organize code. They are kind of code structure which contain global classes and variables. Namespaces are used to split to global code, to structure it better. Here is an example of simple namespace:

namespace Animal
{
   class Mammal
   {
    private:
    String kind;
   
    public:
    void set_kind(String);
   };
}

Now, this is not so wise example, but it helps in grasping this subject. Now you can reach the class Mammal: Animal::Mammal. To create an object for Mammal:

Animal::Mammal object;

However, there is a way to avoid using Animal::Mammal everytime. Simply, after defining the namespace, you can type:

using namespace Animal;

That means you don't have to add Animal:: before every Animal namespace member. A wise usage of namespace is to avoid name conflicts. For example, let's assume that you build classes for your employees, and you split them in cleric workers and field workers. But, you need the same class, employee in both of them:

class employee
{
   private:
   int working_hours;
   int age;
  
   public:
   void set_age(int);
   void set_hours(int);
};

So, you split the code into two namespaces:

namespace Cleric
{
 class employee
 {
   private:
   int working_hours;
   int age;
  
   public:
   void set_age(int);
   void set_hours(int);
 };

//adding some cleric specific classes or variables here
}

namespace Field
{
  class employee
  {
   private:
   int working_hours;
   int age;
  
   public:
   void set_age(int);
   void set_hours(int);
  };

//adding some field worker classes or variables here
}

You can see that we used exactly the same class, employee, here. But, the compiler won't report error because they are in two separate namespaces. You can reach them as Cleric::employee and Field::employee.

STL - Standard Template Library

As you have already read, STL stands for Standard Template Library. It is designed to ease the programming in C++. STL offers few components to the C++ programmer: containers, iterators, algorithms, allocators and function objects. We will discuss the most important of them.

Containers

Containers are objects used to store another type of objects within. The most popular container in the STL library is the vector. It can be very handy when used to work with a lot of objects, since the intput and output are very easy and straightforward. Vectors grows if they are needed to grow. I will explain them through examples:

#include <iostream>
#include <vector> //vectors headers file

int main()
{
   vector<int> integers; //declaration of integers vector

   int first = 5;  //integers to be inserted in the vector
   int second = -2;

   //we use push_back to add values to the end of the vector
  
   vector.push_back(first); //inserting the variables defined above
   vector.push_back(second);
   vector.push_back(10);

   //for example, we will access the third object in, that means the value 10 

   cout <<"The third vector object is: " <<vector[2] <<endl;

   return;
}

Now is time for some explanation. First, we declare the vector:

vector<int> integers;

Then we add the values , using push_back(int):

vector.push_back(first);

Then, we select and print out the third member. Please not that in vectors, the indexing starts at 0, just like in arrays:

cout <<"The third vector object is: " <<vector[2] <<endl;

Some common functions used with vectors are:

size_type size() const;: Used to get the size of the vector.

bool empty() const;: Returns true if the number of elements is zero, otherwise false.

void push_back(const T& x);: We have already used it, it puts members at the end of the vector.

iterator begin();: Returns an iterator that references the beginning of the vector.

iterator end();: Returns an iterator that references a position past the end of the vector.

void erase(iterator first, iterator last);: Erase (remove) elements from a vector.

void clear ();: Clears the whole vector.

Iterators

Iterators serve as pointers. They are objects which point to another object. Iterators are often used in STL containers, such as vectors. They are actually the bridge between the code and the containers. Using iterators, the code points to a specific element in the container. In that way , a simple code could be used on many different containers, such as vectors or linked lists. STL contains a several concept of iterators, several predefined iterators, and of course, functions to operate with them. There are several types of iterators, but most often used are:

  • bidirectional iterators
  • random iterators

Bidirectional operators are used in linked lists, since they have the ability to move forward and backwards. Random operators are used in vectors, arrays and so on. However, there are other types of iterators such as forward iterator, which moves only forward, input iterators which move forward and can only access values, output which also move forward, but it can only read values.

handbook/handbook/programin_3.txt · Last modified: 2010/04/15 21:18 (external edit)