WWW.APPSERVGRID.COM

Phorum about AppServers & Grid Technologies
It is currently Mon Dec 02, 2024 4:41 pm

All times are UTC + 2 hours [ DST ]




Post new topic Reply to topic  [ 1 post ] 
Author Message
PostPosted: Mon Feb 11, 2019 3:09 am 
Offline
Site Admin

Joined: Tue Jan 25, 2011 8:51 pm
Posts: 49
Creating a New Class

It is generally a good practice to define your new classes in separate files. This makes maintaining
and reading the code easier.
To do this, use the following steps in CodeBlocks:
Click File->New->Class...
Give your new class a name, uncheck "Has destructor" and check "Header and implementation file
shall be in same folder", then click the "Create" button.
Note that two new files have been added to your project:
The new files act as templates for our new class.
- MyClass.h is the header file.
- MyClass.cpp is the source file.
----------------------------------------------------------------------------------------------------------
Source & Header

The header file (.h) holds the function declarations (prototypes) and variable declarations.
It currently includes a template for our new MyClass class, with one default constructor.

MyClass.h

#ifndef MYCLASS_H
#define MYCLASS_H
class MyClass
{
public:
MyClass();
protected:
private:
};
#endif // MYCLASS_H

The implementation of the class and its methods go into the source file (.cpp).
Currently it includes just an empty constructor.

MyClass.cpp

#include "MyClass.h"

MyClass::MyClass()
{
//ctor
}

The #ifndef and #define statements in the header file will be discussed in the upcoming lessons.
--------------------------------------------------------------------------------------------------------
Scope Resolution Operator

The double colon in the source file (.cpp) is called the scope resolution operator, and it's
used for the constructor definition:

#include "MyClass.h"
MyClass::MyClass()
{
//ctor
}

The scope resolution operator is used to define a particular class' member functions, which have
already been declared. Remember that we defined the constructor prototype in the header file.
So, basically, MyClass::MyClass() refers to the MyClass() member function - or, in this case,
constructor - of the MyClass class.
-----------------------------------------------------------------------------------------------------------
Source & Header

To use our classes in our main, we need to include the header file.

For example, to use our newly created MyClass in main:
#include <iostream>
#include "MyClass.h"
using namespace std;
int main() {
MyClass obj;
}

The header declares "what" a class (or whatever is being implemented) will do, while the
cpp source file defines "how" it will perform those features.
-------------------------------------------------------------------------------------------------------------
Destructors

Remember constructors? They're special member functions that are automatically called when an object
is created.
Destructors are special functions, as well. They're called when an object is destroyed or deleted.
Objects are destroyed when they go out of scope, or whenever the delete expression is applied to a
pointer directed at an object of a class.
--------------------------------------------------------------------------------------------------------------
Destructors

The name of a destructor will be exactly the same as the class, only prefixed with a tilde (~). A destructor
can't return a value or take any parameters.

class MyClass {
public:
~MyClass() {
// some code
}
};

Destructors can be very useful for releasing resources before coming out of the program. This can include
closing files, releasing memory, and so on.
---------------------------------------------------------------------------------------------------------------
Destructors

For example, let's declare a destructor for our MyClass class, in its header file MyClass.h:

class MyClass
{
public:
MyClass();
~MyClass();
};
----------------------------------------------------------------------------------------------------------------
Destructors
After declaring the destructor in the header file, we can write the implementation in the source file MyClass.cpp:

#include "MyClass.h"
#include <iostream>
using namespace std;
MyClass::MyClass()
{
cout<<"Constructor"<<endl;
}
MyClass::~MyClass()
{
cout<<"Destructor"<<endl;
}

Note that we included the <iostream> header, so that we can use cout.
--------------------------------------------------------------------------------------------------------------------
Destructors

Since destructors can't take parameters, they also can't be overloaded.
Each class will have just one destructor.
Defining a destructor is not mandatory; if you don't need one, you don't have to define one.
--------------------------------------------------------------------------------------------------------------------
Destructors

Let's return to our main.

#include <iostream>
#include "MyClass.h"
using namespace std;
int main() {
MyClass obj;
return 0;
}

We included the class' header file and then created an object of that type.

This returns the following output:
Constructor
Destructor

When the program runs, it first creates the object and calls the constructor. The object is deleted
and the destructor is called when the program's execution is completed.
Remember that we printed "Constructor" from the constructor and "Destructor" from the destructor.
---------------------------------------------------------------------------------------------------------------------
#ifndef & #define

We created separate header and source files for our class, which resulted in this header file.

#ifndef MYCLASS_H
#define MYCLASS_H
class MyClass
{
public:
MyClass();
protected:
private:
};

#endif // MYCLASS_H

ifndef stands for "if not defined". The first pair of statements tells the program to define
the MyClass header file if it has not been defined already.
endif ends the condition.
This prevents a header file from being included more than once within one file.
------------------------------------------------------------------------------------------------------------------------
Member Functions

Let's create a sample function called myPrint() in our class.

MyClass.h

class MyClass
{
public:
MyClass();
void myPrint();
};

MyClass.cpp

#include "MyClass.h"
#include <iostream>
using namespace std;
MyClass::MyClass() {
}
void MyClass::myPrint() {
cout <<"Hello"<<endl;
}

Since myPrint() is a regular member function, it's necessary to specify its return type in both the declaration
and the definition.
-------------------------------------------------------------------------------------------------------------------------
Dot Operator

Next, we'll create an object of the type MyClass, and call its myPrint() function using the dot (.) operator:

#include "MyClass.h"
int main() {
MyClass obj;
obj.myPrint();
}

// Outputs "Hello"
--------------------------------------------------------------------------------------------------------------------------
Pointers

We can also use a pointer to access the object's members.
The following pointer points to the obj object:

MyClass obj;
MyClass *ptr = &obj;

The type of the pointer is MyClass, as it points to an object of that type.
--------------------------------------------------------------------------------------------------------------------------
Fill in the blanks to declare a pointer to ''obj'':
Sally obj;
Sally *sallyPtr = &obj;
-------------------------------------------------------------------------------------------------------------------------
Selection Operator

The arrow member selection operator (->) is used to access an object's members with a pointer.

MyClass obj;
MyClass *ptr = &obj;
ptr->myPrint();

When working with an object, use the dot member selection operator (.).
When working with a pointer to the object, use the arrow member selection operator (->).
--------------------------------------------------------------------------------------------------------------------------
Type in the missing arrow member selection operator to call myPrint() function via sallyPtr.
Sally obj;
Sally *sallyPtr = &obj;
sallyPtr -> myPrint();
----------------------------------------------------------------------------------------------
Constants

A constant is an expression with a fixed value. It cannot be changed while the program is running.
Use the const keyword to define a constant variable.

const int x = 42;

All constant variables must be initialized at the time of their creation.
--------------------------------------------------------------------------------------------------------
Constant Objects

As with the built-in data types, we can make class objects constant by using the const keyword.

const MyClass obj;

All const variables must be initialized when they're created. In the case of classes, this
initialization is done via constructors. If a class is not initialized using a parameterized
constructor, a public default constructor must be provided - if no public default constructor
is provided, a compiler error will occur.
Once a const class object has been initialized via the constructor, you cannot modify the
object's member variables. This includes both directly making changes to public member
variables and calling member functions that set the value of member variables.
When you've used const to declare an object, you can't change its data members during the
object's lifetime.

---------------------------------------------------------------------------------------------------------
Constant Objects

Only non-const objects can call non-const functions.
A constant object can't call regular functions. Hence, for a constant object to work you need a
constant function.

To specify a function as a const member, the const keyword must follow the function prototype,
outside of its parameters' closing parenthesis. For const member functions that are defined
outside of the class definition, the const keyword must be used on both the function prototype
and definition. For example:

MyClass.h

class MyClass
{
public:
void myPrint() const;
};

MyClass.cpp

#include "MyClass.h"
#include <iostream>
using namespace std;
void MyClass::myPrint() const {
cout <<"Hello"<<endl;
}

Now the myPrint() function is a constant member function. As such, it can be called by our constant object:

int main() {
const MyClass obj;
obj.myPrint();
}
// Outputs "Hello"
------------------------------------------------------------------------------------------------------------
Type in the missing keywords to declare a constant printAge() member function for the Student class.

class Student {
public:
void printAge() const;
};
-----------------------------------------------------------------------------------------------
Constant Objects

Attempting to call a regular function from a constant object results in an error.
In addition, a compiler error is generated when any const member function attempts to
change a member variable or to call a non-const member function.
Defining constant objects and functions ensures that corresponding data members
cannot be unexpectedly modified.
------------------------------------------------------------------------------------------------
Member Initializers

Recall that constants are variables that cannot be changed, and that all const variables
must be initialized at time of creation.
C++ provides a handy syntax for initializing members of the class called the member
initializer list (also called a constructor initializer).
-------------------------------------------------------------------------------------------------
Member Initializers

Consider the following class:

class MyClass {
public:
MyClass(int a, int b) {
regVar = a;
constVar = b;
}
private:
int regVar;
const int constVar;
};

This class has two member variables, regVar and constVar. It also has a constructor that
takes two parameters, which are used to initialize the member variables.
Running this code returns an error, because one of its member variables is a constant,
which cannot be assigned a value after declaration.
In cases like this one, a member initialization list can be used to assign values
to the member variables.

class MyClass {
public:
MyClass(int a, int b)
: regVar(a), constVar(b)
{
}
private:
int regVar;
const int constVar;
};

Note that in the syntax, the initialization list follows the constructor parameters.
The list begins with a colon (:), and then lists each variable to be initialized,
along with the value for that variable, with a comma to separate them.
Use the syntax variable(value) to assign values.
The initialization list eliminates the need to place explicit assignments in the
constructor body. Also, the initialization list does not end with a semicolon.
-------------------------------------------------------------------------------------------
You have a class ''Student'' with two members, "age" and "num". Fill in the blanks
to initialize those members in the constructor initializer list with the corresponding values.
Student::Student(int a, int b)
:age(a),
num (b) {
}
--------------------------------------------------------------------------------------------
Member Initializers

Let's write the previous example using separate header and source files.

MyClass.h

class MyClass {
public:
MyClass(int a, int b);
private:
int regVar;
const int constVar;
};

MyClass.cpp

MyClass::MyClass(int a, int b)
: regVar(a), constVar(b)
{
cout << regVar << endl;
cout << constVar << endl;
}

We have added cout statements in the constructor to print the values of the member variables.
Our next step is to create an object of our class in main, and use the constructor to assign values.

#include "MyClass.h"
int main() {
MyClass obj(42, 33);
}

/*Outputs
42
33
*/

The constructor is used to create the object, assigning two parameters to the member variables
via the member initialization list.
-------------------------------------------------------------------------------------------------------
Member Initializers

The member initialization list may be used for regular variables, and must be used for constant
variables.
Even in cases in which member variables are not constant, it makes good sense to use the
member initializer syntax.
------------------------------------------------------------------------------------------------------
Composition

In the real world, complex objects are typically built using smaller, simpler objects.
For example, a car is assembled using a metal frame, an engine, tires, and a large number
of other parts. This process is called composition.
In C++, object composition involves using classes as member variables in other classes.
This sample program demonstrates composition in action. It contains Person and Birthday
classes, and each Person will have a Birthday object as its member.

Birthday:

class Birthday {
public:
Birthday(int m, int d, int y)
: month(m), day(d), year(y)
{
}
private:
int month;
int day;
int year;
};

Our Birthday class has three member variables. It also has a constructor that initializes
the members using a member initialization list.
The class was declared in a single file for the sake of simplicity. Alternatively, you
could use header and source files.
-------------------------------------------------------------------------------------------------------
Composition

Let's also add a printDate() function to our Birthday class:

class Birthday {
public:
Birthday(int m, int d, int y)
: month(m), day(d), year(y)
{
}
void printDate()
{
cout<<month<<"/"<<day
<<"/"<<year<<endl;
}
private:
int month;
int day;
int year;
};
---------------------------------------------------------------------------------------------------------------
Composition

Next, we can create the Person class, which includes the Birthday class.

#include <string>
#include "Birthday.h"
class Person {
public:
Person(string n, Birthday b)
: name(n),
bd(b)
{
}
private:
string name;
Birthday bd;
};

The Person class has a name and a Birthday member, and a constructor to initialize them.
Ensure that the corresponding header files are included.
----------------------------------------------------------------------------------------------------------------
Composition

Now, our Person class has a member of type Birthday:

class Person {
public:
Person(string n, Birthday b)
: name(n),
bd(b)
{
}
private:
string name;
Birthday bd;
};

Composition is used for objects that share a has-a relationship, as in "A Person has a Birthday".
------------------------------------------------------------------------------------------------------------------
Composition

Let's add a printInfo() function to our Person class, that prints the data of the object:

class Person {
public:
Person(string n, Birthday b)
: name(n),
bd(b)
{
}
void printInfo()
{
cout << name << endl;
bd.printDate();
}
private:
string name;
Birthday bd;
};

Notice that we can call the bd member's printDate() function, since it's of type Birthday, which has
that function defined.
---------------------------------------------------------------------------------------------------------------------
Composition

Now that we've defined our Birthday and Person classes, we can go to our main, create a Birthday object,
and then pass it to a Person object.

int main() {
Birthday bd(2, 21, 1985);
Person p("David", bd);
p.printInfo();
}

/*Outputs
David
2/21/1985
*/

We've created a Birthday object for the date of 2/21/1985. Next, we created a Person object and passed
the Birthday object to its constructor. Finally, we used the Person object's printInfo() function to
print its data.
In general, composition serves to keep each individual class relatively simple, straightforward,
and focused on performing one task. It also enables each sub-object to be self-contained, allowing
for reusability (we can use the Birthday class within various other classes).
---------------------------------------------------------------------------------------------------------------------
Friend Functions

Normally, private members of a class cannot be accessed from outside of that class.
However, declaring a non-member function as a friend of a class allows it to access the class' private
members. This is accomplished by including a declaration of this external function within the class, and
preceding it with the keyword friend.
In the example below, someFunc(), which is not a member function of the class, is a friend of MyClass
and can access its private members.

class MyClass {
public:
MyClass() {
regVar = 0;
};
private:
int regVar;

friend void someFunc(MyClass &obj);
};

Note that when passing an object to the function, we need to pass it by reference, using the & operator.
-------------------------------------------------------------------------------------------------------------------------
Friend Functions

The function someFunc() is defined as a regular function outside the class. It takes an object of type MyClass
as its parameter, and is able to access the private data members of that object.

class MyClass {
public:
MyClass() {
regVar = 0;
};
private:
int regVar;
friend void someFunc(MyClass &obj);
};
void someFunc(MyClass &obj) {
obj.regVar = 42;
cout << obj.regVar;
}

The someFunc() function changes the private member of the object and prints its value.
To make its members accessible, the class has to declare the function as a friend in its definition.
You cannot "make" a function a friend to a class without the class "giving away" its friendship to that function.
-------------------------------------------------------------------------------------------------------------------------
Friend Functions

Now we can create an object in main and call the someFunc() function:
int main() {
MyClass obj;
someFunc(obj);
}

//Outputs 42

someFunc() had the ability to modify the private member of the object and print its value.
Typical use cases of friend functions are operations that are conducted between two different classes accessing
private members of both.
You can declare a function friend across any number of classes.
Similar to friend functions, you can define a friend class, which has access to the private members of another class.
--------------------------------------------------------------------------------------------------------------------------
This

Every object in C++ has access to its own address through an important pointer called the this pointer.
Inside a member function this may be used to refer to the invoking object.
Let's create a sample class:

class MyClass {
public:
MyClass(int a) : var(a)
{ }
private:
int var;
};

Friend functions do not have a this pointer, because friends are not members of a class.
--------------------------------------------------------------------------------------------------------------------------
This

The printInfo() method offers three alternatives for printing the member variable of the class.

class MyClass {
public:
MyClass(int a) : var(a)
{ }
void printInfo() {
cout << var<<endl;
cout << this->var<<endl;
cout << (*this).var<<endl;
}
private:
int var;
};

All three alternatives will produce the same result.
this is a pointer to the object, so the arrow selection operator is used to select the member variable.
---------------------------------------------------------------------------------------------------------------------------
This

To see the result, we can create an object of our class and call the member
function.

#include <iostream>
using namespace std;
class MyClass {
public:
MyClass(int a) : var(a)
{ }
void printInfo() {
cout << var <<endl;
cout << this->var <<endl;
cout << (*this).var <<endl;
}
private:
int var;
};

int main() {
MyClass obj(42);
obj.printInfo();
}

/* Outputs
42
42
42
*/

All three of the ways to access the member variable work.
Note that only member functions have a this pointer.
----------------------------------------------------------------------------------
This

You may be wondering why it's necessary to use the this keyword, when you have
the option of directly specifying the variable.
The this keyword has an important role in operator overloading, which will be
covered in the following lesson.
-----------------------------------------------------------------------------------
Operator Overloading

Most of the C++ built-in operators can be redefined or overloaded.
Thus, operators can be used with user-defined types as well (for example, allowing
you to add two objects together).
Operators that can't be overloaded include :: | .* | . | ?:
-------------------------------------------------------------------------------------
Operator Overloading

Let's declare a sample class to demonstrate operator overloading:

class MyClass {
public:
int var;
MyClass() {}
MyClass(int a)
: var(a) { }
};

Our class has two constructors and one member variable.
We will be overloading the + operator, to enable adding two objects of our class
together.
---------------------------------------------------------------------------------------
Operator Overloading

Overloaded operators are functions, defined by the keyword operator followed
by the symbol for the operator being defined.
An overloaded operator is similar to other functions in that it has a return
type and a parameter list.

In our example we will be overloading the + operator. It will return an
object of our class and take an object of our class as its parameter.

class MyClass {
public:
int var;
MyClass() {}
MyClass(int a)
: var(a) { }
MyClass operator+(MyClass &obj) {
}
};

Now, we need to define what the function does.
-------------------------------------------------------------------------------------------
Operator Overloading

We need our + operator to return a new MyClass object with a member variable equal
to the sum of the two objects' member variables.

class MyClass {
public:
int var;
MyClass() {}
MyClass(int a)
: var(a) { }

MyClass operator+(MyClass &obj) {
MyClass res;
res.var= this->var+obj.var;
return res;
}
};

Here, we declared a new res object. We then assigned the sum of the member variables
of the current object (this) and the parameter object (obj) to the res object's var member
variable. The res object is returned as the result.
This gives us the ability to create objects in main and use the overloaded + operator to
add them together.

int main() {
MyClass obj1(12), obj2(55);
MyClass res = obj1+obj2;

cout << res.var;
}

//Outputs 67

With overloaded operators, you can use any custom logic needed. However, it's not possible
to alter the operators' precedence, grouping, or number of operands.
----------------------------------------------------------------------------------------------------
Fill in the blanks to define an overloaded + operator for the class ''Test''.
Test Test::operator+ (Test obj) {
Test newObj;
newObj.mem =
mem + obj.mem;
return newObj;
}
----------------------------------------------------------------------------------------------------
Fill in the blanks to declare a pointer to the ''st'', where ''st'' is of type ''Student'', then
call printAge() via the pointer.
Student st;
Student * stPtr = &st;
stPtr -> printAge();
------------------------------------------------------------------------------------------------------
For a class ''Test'' with two private members named ''mem'' and ''mem2'', fill in the
blanks to print out their values in the printValues() function using the ''this'' keyword.

void Test::printValues() {
cout << this -> mem;
cout << this -> mem2;
}
-------------------------------------------------------------------------------------------------------
Inheritance

Inheritance is one of the most important concepts of object-oriented programming.
Inheritance allows us to define a class based on another class. This facilitates greater
ease in creating and maintaining an application.
The class whose properties are inherited by another class is called the Base class.
The class which inherits the properties is called the Derived class. For example, the
Daughter class (derived) can be inherited from the Mother class (base).
The derived class inherits all feature from the base class, and can have its own additional features.

The idea of inheritance implements the is a relationship. For example, mammal IS-A animal,
dog IS-A mammal, hence dog IS-A animal as well.
---------------------------------------------------------------------------------------------------------
Inheritance

To demonstrate inheritance, let's create a Mother class and a Daughter class:

class Mother
{
public:
Mother() {};
void sayHi() {
cout << "Hi";
}
};

class Daughter
{
public:
Daughter() {};
};

The Mother class has a public method called sayHi().
The next step is to inherit (derive) the Daughter from the Mother.
----------------------------------------------------------------------------------------------------------
Inheritance

This syntax derives the Daughter class from the Mother class.

class Daughter : public Mother
{
public:
Daughter() {};
};

The Base class is specified using a colon and an access specifier: public means, that all public
members of the base class are public in the derived class.
In other words, all public members of the Mother class become public members of the Daughter class.
------------------------------------------------------------------------------------------------------------
Inheritance

As all public members of the Mother class become public members for the Daughter class, we
can create an object of type Daughter and call the sayHi() function of the Mother class
for that object:

#include <iostream>
using namespace std;
class Mother
{
public:
Mother() {};
void sayHi() {
cout << "Hi";
}
};

class Daughter: public Mother
{
public:
Daughter() {};
};

int main() {
Daughter d;
d.sayHi();
}
//Outputs "Hi"

A derived class inherits all base class methods with the following exceptions:
- Constructors, destructors
- Overloaded operators
- The friend functions
A class can be derived from multiple classes by specifying the base classes in a
comma-separated list. For example: class Daughter: public Mother, public Father
--------------------------------------------------------------------------------------------
Access Specifiers

Up to this point, we have worked exclusively with public and private access specifiers.
Public members may be accessed from anywhere outside of the class, while access to
private members is limited to their class and friend functions.
As we've seen previously, it's a good practice to use public methods to access private
class variables.
--------------------------------------------------------------------------------------------
Protected

There is one more access specifier - protected.
A protected member variable or function is very similar to a private member, with
one difference - it can be accessed in the derived classes.

class Mother {
public:
void sayHi() {
cout << var;
}

private:
int var=0;

protected:
int someVar;
};

Now someVar can be accessed by any class that is derived from the Mother class.
----------------------------------------------------------------------------------------------
Type of Inheritance

Access specifiers are also used to specify the type of inheritance.
Remember, we used public to inherit the Daughter class:

class Daughter: public Mother

private and protected access specifiers can also be used here.

Public Inheritance: public members of the base class become public members of the
derived class and protected members of the base class become protected members of the derived
class. A base class's private members are never accessible directly from a derived class, but can
be accessed through calls to the public and protected members of the base class.
Protected Inheritance: public and protected members of the base class become protected members of the
derived class.
Private Inheritance: public and protected members of the base class become private members of
the derived class. Public inheritance is the most commonly used inheritance type.
If no access specifier is used when inheriting classes, the type becomes private by default.
------------------------------------------------------------------------------------------------
Fill in the blanks to declare a protected member in the Base class and to access it from
the Derived class' ''foo'' function.

class Base {
protected :
int baseVar;
};
class Derived : public Base {
public:
void foo() {
baseVar = 12;
}
};
-------------------------------------------------------------------------------------------------
Inheritance

When inheriting classes, the base class' constructor and destructor are not inherited.
However, they are being called when an object of the derived class is created or deleted.

To further explain this behavior, let's create a sample class that includes a
constructor and a destructor:

class Mother {
public:
Mother()
{
cout <<"Mother ctor"<<endl;
}
~Mother()
{
cout <<"Mother dtor"<<endl;
}
};

Creating an object in main results in the following output:

int main() {
Mother m;
}
/* Outputs
Mother ctor
Mother dtor
*/

The object is created and then deleted, when the program finishes to run.
--------------------------------------------------------------------------------------------------
Fill in the blanks to define a constructor and a destructor for the ''Mother'' class.
Mother ::Mother() {
cout << "constructor" << endl;
}
Mother::~Mother () {
cout << "destructor" << endl;
}
--------------------------------------------------------------------------------------------------
Inheritance

Next, let's create a Daughter class, with its own constructor and destructor, and make
it a derived class of the Mother:

class Daughter: public Mother {
public:
Daughter()
{
cout <<"Daughter ctor"<<endl;
}
~Daughter()
{
cout <<"Daughter dtor"<<endl;
}
};
----------------------------------------------------------------------------------------------------
Inheritance

Now, what happens when we create a Daughter object?

int main() {
Daughter m;
}

/*Outputs
Mother ctor
Daughter ctor
Daughter dtor
Mother dtor
*/

Note that the base class' constructor is called first, and the derived class'
constructor is called next.
When the object is destroyed, the derived class's destructor is called, and
then the base class' destructor is called.
You can think of it as the following: The derived class needs its base class
in order to work - that is why the base class is set up first.
------------------------------------------------------------------------------------------------------
Summary

Constructors
The base class constructor is called first.

Destructors
The derived class destructor is called first, and then the base class destructor gets called.
This sequence makes it possible to specify initialization and de-initialization scenarios for
your derived classes.
-------------------------------------------------------------------------------------------------------
Polymorphism

The word polymorphism means "having many forms".
Typically, polymorphism occurs when there is a hierarchy of classes and they are related by inheritance.

C++ polymorphism means that a call to a member function will cause a different implementation to
be executed depending on the type of object that invokes the function.
Simply, polymorphism means that a single function can have a number of different implementations.
--------------------------------------------------------------------------------------------------------
Polymorphism

Polymorphism can be demonstrated more clearly using an example:
Suppose you want to make a simple game, which includes different enemies: monsters, ninjas, etc.
All enemies have one function in common: an attack function. However, they each attack in a
different way. In this situation, polymorphism allows for calling the same attack function
on different objects, but resulting in different behaviors.

The first step is to create the Enemy class.

class Enemy {
protected:
int attackPower;
public:
void setAttackPower(int a){
attackPower = a;
}
};

Our Enemy class has a public method called setAttackPower, which sets the protected attackPower member
variable.
---------------------------------------------------------------------------------------------------------
Polymorphism

Our second step is to create classes for two different types of enemies, Ninjas and Monsters.
Both of these new classes inherit from the Enemy class, so each has an attack power. At the same
time, each has a specific attack function.

class Ninja: public Enemy {
public:
void attack() {
cout << "Ninja! - "<<attackPower<<endl;
}
};

class Monster: public Enemy {
public:
void attack() {
cout << "Monster! - "<<attackPower<<endl;
}
};

As you can see, their individual attack functions differ.
Now we can create our Ninja and Monster objects in main.
int main() {
Ninja n;
Monster m;
}

Ninja and Monster inherit from Enemy, so all Ninja and Monster objects are Enemy objects.
This allows us to do the following:

Enemy *e1 = &n;
Enemy *e2 = &m;

We've now created two pointers of type Enemy, pointing them to the Ninja and Monster objects.
--------------------------------------------------------------------------------------------------------
Polymorphism

Now, we can call the corresponding functions:
int main() {
Ninja n;
Monster m;
Enemy *e1 = &n;
Enemy *e2 = &m;

e1->setAttackPower(20);
e2->setAttackPower(80);

n.attack();
m.attack();
}

/* Output:
Ninja! - 20
Monster! - 80
*/

We would have achieved the same result by calling the functions directly on the objects.
However, it's faster and more efficient to use pointers.
Also, the pointer demonstrates, that you can use the Enemy pointer without actually
knowing that it contains an object of the subclass.
---------------------------------------------------------------------------------------------------------
Virtual Functions

The previous example demonstrates the use of base class pointers to the derived classes.
Why is that useful? Continuing on with our game example, we want every Enemy to have an attack() function.
To be able to call the corresponding attack() function for each of the derived classes
using Enemy pointers, we need to declare the base class function as virtual.
Defining a virtual function in the base class, with a corresponding version in a derived
class, allows polymorphism to use Enemy pointers to call the derived classes' functions.
Every derived class will override the attack() function and have a separate implementation:

class Enemy {
public:
virtual void attack() {
}
};

class Ninja: public Enemy {
public:
void attack() {
cout << "Ninja!"<<endl;
}
};

class Monster: public Enemy {
public:
void attack() {
cout << "Monster!"<<endl;
}
};

A virtual function is a base class function that is declared using the keyword virtual.
------------------------------------------------------------------------------------------------------------
Virtual Functions

Now, we can use Enemy pointers to call the attack() function.
int main() {
Ninja n;
Monster m;
Enemy *e1 = &n;
Enemy *e2 = &m;

e1->attack();
e2->attack();
}

/* Output:
Ninja!
Monster!
*/

As the attack() function is declared virtual, it works like a template, telling that
the derived class might have an attack() function of its own.
------------------------------------------------------------------------------------------------------------
Virtual Functions

Our game example serves to demonstrate the concept of polymorphism; we are using Enemy pointers
to call the same attack() function, and generating different results.

e1->attack();
e2->attack();

If a function in the base class is virtual, the function's implementation in the derived
class is called according to the actual type of the object referred to, regardless of the declared
type of the pointer.
A class that declares or inherits a virtual function is called a polymorphic class.
-------------------------------------------------------------------------------------------------------------
Virtual Functions

Virtual functions can also have their implementation in the base class:
class Enemy {
public:
virtual void attack() {
cout << "Enemy!"<<endl;
}
};

class Ninja: public Enemy {
public:
void attack() {
cout << "Ninja!"<<endl;
}
};

class Monster: public Enemy {
public:
void attack() {
cout << "Monster!"<<endl;
}
};

Now, when you create an Enemy pointer, and call the attack() function, the compiler
will call the function, which corresponds to the object's type, to which the pointer points:

int main() {
Ninja n;
Monster m;
Enemy e;

Enemy *e1 = &n;
Enemy *e2 = &m;
Enemy *e3 = &e;

e1->attack();
// Outputs "Ninja!"

e2->attack();
// Outputs "Monster!"

e3->attack();
// Outputs "Enemy!"
}

This is how polymorphism is generally used. You have different classes with a function
of the same name, and even the same parameters, but with different implementations.
--------------------------------------------------------------------------------------------------
Pure Virtual Function

In some situations you'd want to include a virtual function in a base class so that it
may be redefined in a derived class to suit the objects of that class, but that there
is no meaningful definition you could give for the function in the base class.
The virtual member functions without definition are known as pure virtual functions.
They basically specify that the derived classes define that function on their own.
The syntax is to replace their definition by =0 (an equal sign and a zero):

class Enemy {
public:
virtual void attack() = 0;
};

The = 0 tells the compiler that the function has no body.
----------------------------------------------------------------------------------------------------
Pure Virtual Functions

A pure virtual function basically defines, that the derived classes will have that function defined on their own.
Every derived class inheriting from a class with a pure virtual function must override that function.
If the pure virtual function is not overridden in the derived class, the code fails to compile and
results in an error when you try to instantiate an object of the derived class.
-----------------------------------------------------------------------------------------------------
Pure Virtual Functions

The pure virtual function in the Enemy class must be overridden in its derived classes.

class Enemy {
public:
virtual void attack() = 0;
};

class Ninja: public Enemy {
public:
void attack() {
cout << "Ninja!"<<endl;
}
};

class Monster: public Enemy {
public:
void attack() {
cout << "Monster!"<<endl;
}
};
-------------------------------------------------------------------------------------------------------
Abstract Classes

You cannot create objects of the base class with a pure virtual function.
Running the following code will return an error:
Enemy e; // Error
These classes are called abstract. They are classes that can only be used as base classes, and
thus are allowed to have pure virtual functions.
You might think that an abstract base class is useless, but it isn't.
It can be used to create pointers and take advantage of all its polymorphic abilities.
For example, you could write:

Ninja n;
Monster m;
Enemy *e1 = &n;
Enemy *e2 = &m;

e1->attack();
e2->attack();

In this example, objects of different but related types are referred to using a unique type of
pointer (Enemy*), and the proper member function is called every time, just because they are virtual.
--------------------------------------------------------------------------------------------------------
Function Templates

Functions and classes help to make programs easier to write, safer, and more maintainable.
However, while functions and classes do have all of those advantages, in certain cases they can
also be somewhat limited by C++'s requirement that you specify types for all of your parameters.
For example, you might want to write a function that calculates the sum of two numbers, similar to this:

int sum(int a, int b) {
return a+b;
}
---------------------------------------------------------------------------------------------------------
Function Templates

We can now call the function for two integers in our main.

int sum(int a, int b) {
return a+b;
}

int main () {
int x=7, y=15;
cout << sum(x, y) << endl;
}
// Outputs 22

The function works as expected, but is limited solely to integers.
----------------------------------------------------------------------------------------------------------
Function Templates

It becomes necessary to write a new function for each new type, such as doubles.

double sum(double a, double b) {
return a+b;
}

Wouldn't it be much more efficient to be able to write one version of sum() to work with parameters
of any type?
Function templates give us the ability to do that!
With function templates, the basic idea is to avoid the necessity of specifying an exact type
for each variable. Instead, C++ provides us with the capability of defining functions using
placeholder types, called template type parameters.
To define a function template, use the keyword template, followed by the template type definition:

template <class T>

We named our template type T, which is a generic data type.
------------------------------------------------------------------------------------------------------------
Function Templates

Now we can use our generic data type T in the function:

template <class T>
T sum(T a, T b) {
return a+b;
}

int main () {
int x=7, y=15;
cout << sum(x, y) << endl;
}

// Outputs 22

The function returns a value of the generic type T, taking two parameters, also of type T.
Our new function worked exactly as the previous one for integer values did.
-------------------------------------------------------------------------------------------------------------
Function Templates

The same function can be used with other data types, for example doubles:
template <class T>
T sum(T a, T b) {
return a+b;
}

int main () {
double x=7.15, y=15.54;
cout << sum(x, y) << endl;
}
// Outputs 22.69

The compiler automatically calls the function for the corresponding type.
When creating a template type parameter, the keyword typename may be used as an alternative
to the keyword class: template <typename T>.
In this context, the keywords are identical, but throughout this course, we'll use the
keyword class.
---------------------------------------------------------------------------------------------------------------
Function Templates

Template functions can save a lot of time, because they are written only once, and work with different types.
Template functions reduce code maintenance, because duplicate code is reduced significantly.
Enhanced safety is another advantage in using template functions, since it's not necessary to manually
copy functions and change types.
---------------------------------------------------------------------------------------------------------------
Function Templates

Function templates also make it possible to work with multiple generic data types. Define the data types
using a comma-separated list.
Let's create a function that compares arguments of varying data types (an int and a double), and prints
the smaller one.

template <class T, class U>

As you can see, this template declares two different generic data types, T and U.
----------------------------------------------------------------------------------------------------------------
Function Templates

Now we can continue with our function declaration:
template <class T, class U>
T smaller(T a, U b) {
return (a < b ? a : b);
}

The ternary operator checks the a<b condition and returns the corresponding result. The expression
(a < b ? a : b) is equivalent to the expression if a is smaller than b, return a, else, return b.
------------------------------------------------------------------------------------------------------------------
Function Templates

In our main, we can use the function for different data types:
template <class T, class U>
T smaller(T a, U b) {
return (a < b ? a : b);
}

int main () {
int x=72;
double y=15.34;
cout << smaller(x, y) << endl;
}

// Outputs 15

The output converts to an integer, because we specified the function template's return type to be of the
same type as the first parameter (T), which is an integer.
-------------------------------------------------------------------------------------------------------------------
Function Templates

T is short for Type, and is a widely used name for type parameters.
It's not necessary to use T, however; you can declare your type parameters using any identifiers that work for you.
The only terms you need to avoid are C++ keywords.
Remember that when you declare a template parameter, you absolutely must use it in your function definition.
Otherwise, the compiler will complain!


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 1 post ] 

All times are UTC + 2 hours [ DST ]


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron