C++ (BEGINNER_TO_BEYOND) --- (Part-3)

 


I prefer you to run the code in the debugger of the compiler and check how line by line control passes from one point to the another point and that will help you to understand the code more clearly, so do follow the instructions.

CLASSES AND OBJECTS:

Procedural Programming:

Focus is on processes or actions that a program takes, programs are typically a collection of functions, data is declared separately, data is passed as arguments into functions.

Object Oriented Programming:

Object oriented is all about modelling your software in terms of classes and objects.

It's all about abstraction as our program grow more and more complex then we need to deal with the complexity that's why classes and objects are one way to do just that.

Characteristics of OOPs:

1. Classes and objects

2. Encapsulation

3. Information hiding: using access modifiers

4. Reusability

5. Inheritance

6. Polymorphism


Procedural Programming:

Focus is on processes or actions that a program takes, programs are typically a collection of functions, data is declared separately, data is passed as arguments into functions.


Classes And Objects:

Classes:

1.Blueprint from which objects are created

2. User defined

3. Has attributes(data), methods(functions)

4. Can hide data and methods

5. provides a public interface


Objects:

1. Created from a class

2. Represents a specific instance of class

3. Class can have many objects

4. Each object has its own identity

Since C++ supports encapsulation that means we can also provide methods or function in the class body that is specific to the particular class.


Declaring Class and Objects:

#include <iostream>

#include<string>

#include<vector>

using namespace std;

 

class Player  //declaring class

{

    //attributes

    string name;

    int health;

    int exp;

    //methods

    void talk();  //functions prototypes

    bool i_dead();

   

};  //terminating class

int main()

{

    Player anubhav;  //class_name object_name, anubhav is object name

    Player suman;

   

    Player players[] {anubhav,suman};  //array of Player object

   

    vector<Player> player_vec{anubhav};  //collection of Player objects

    player_vec.push_back(suman);  //object suman is added to player_vec

   

    Player *enemy{};  //enemy is not an object, enemy is the pointer to player object, enemy is pointing to the player object

    enemy = new Player;  //create the enemy on the heap that enemy object which is a player

    delete enemy;  //free up space in heap

   

    return 0;

}


Accessing Class Methods:

Accessing attributes and methods of a class.

If we have a pointer to an object then we need to dereference the pointer first in order to get to that object. In order to access the class members we have to dereference the pointer using asterisk and then use the dot operator to get the class.

Member of pointer operator (->) arrow operator.


#include <iostream>

#include<string>

#include<vector>

using namespace std;

class Player

{

    public:

    string name;

    int health;

    int exp;

   

    void talk(string text_to_say)

    {

        cout<<name<<" says "<<text_to_say<<endl;

    }

    bool i_dead();

}; 

 

class Account

{

    public:

   

    string name;

    double balance;

   

    bool deposit(double bal)

    {

        bal += balance;

        cout<<"Balance is: "<<bal<<endl;

    }

    bool withdraw(double bal)

    {

        bal -= balance;

        cout<<"Balance available: "<<bal<<endl;

    }

};

int main()

{

    Player anubhav;

    anubhav.name = "Anubhav";  //dot operator is used to access attributes and methods of class via object

    anubhav.health = 98;

    anubhav.exp = 12;

    anubhav.talk("How are u ?");

   

    Player *enemy = new Player; //enemy is the pointer to player object, that is made dynamically on heap

    enemy->name = "Suman";  //go to the object that enemy is pointing to and set the name to "suman"

    enemy->talk("I'll kill you");

   

    Account anu_acc;

    anu_acc.name = "Anubhav Account";

    anu_acc.balance = 10000;

    anu_acc.deposit(5000);

    anu_acc.withdraw(400);

   

    return 0;

}


Access Modifiers:

Public: accessible everywhere

Private: accessible only by members or friends of the class

Protected: used in inheritance, we'll talk about that later


#include <iostream>

#include<string>

#include<vector>

using namespace std;


class Player

{

    private:

    string name;

    int health;

    int exp;

   

    public:

    void talk(string text_to_say)

    {

        cout<<name<<" says "<<text_to_say<<endl;

    }

    bool i_dead();

   

}; 

 

int main()

{

    Player object1;

    object1.name = "anubhav";  //error occurs cz we are accessing private attribute

    object1.talk("Hey there");

   

    return 0;

}


Implementing Member Methods:

Very similar to implementing functions, member methods have access to member attributes, can be implemented outside the class declaration need to use (class_name :: method_name).

In C++ we can separate classes specification from its implementation for this we use (.h) file for the class declaration and (.cpp) file for the class implementation.

Separating specification from implementation, we create a file name class_name.h this is an include or header file that will pass and include in our data files whenever we need to use the particular class. Notice that in this file ,we provided the specification for the account class. Include guard used for data duplicity if class used multiple times.


include<iostream> is used to include system header files and the compiler knows where these are located

include with double quotes tell the compiler to include header files that are local to this project, the compiler also knows were they are.

The specification is in class_name.h and the implementation is in class_name.cpp.


#include <iostream>

#include<string>

#include<vector>

using namespace std;

 

class Account

{

private:

    string name;

    double balance;

 

public:

    void set_balance(double bal){balance = bal;}  //inline functions declared and defined within the class

    double get_balance(){return balance;}

   

    void set_name(string n);  //function will be defined outside class

    string get_name();

    bool deposit(double amount);

    bool withdraw(double amount);

};

 

void Account :: set_name(string n)  //function defined outside class using scope resolution

{

    name = n;                                 //return type is also provided

}

string Account :: get_name()

{

    return name;

}

bool Account :: deposit(double amount)

{

    balance += amount;

    return true;  //this function will return boolean only

}

bool Account :: withdraw(double amount)

{

    if(balance-amount >= 0)

    {

        balance -= amount;

        return true;

    }

    else

    {

        return false;

    }

}

int main()

{

    Account object;

    object.set_name("Anubhav");

    object.set_balance (1000.0);

   

    if(object.deposit(200.0))

        cout<<"Deposit OK"<<endl;

    else

        cout<<"Deposit not allowed";

 

    if(object.withdraw(500.0))

        cout<<"Withdrwal OK"<<endl;

    else

        cout<<"Not sufficient fund"<<endl;

 

    if(object.withdraw(1500.0))

        cout<<"Withdrwal OK"<<endl;

    else

        cout<<"Not sufficient fund"<<endl;

       

    return 0;

}


Constructor and Destructor:

Constructor are special member method that are invoked during object creation, useful for initialization, same name as class, no return type and can be overloaded.

Destructor special member method, same name as the class proceeded with a tilde(~), invoked automatically when an object is destroyed, no return type and no parameters, only 1 destructor allows per class and cannot be overloaded and useful for release memory and other resources.

The destructor is called automatically when a local object goes out of scope or we delete a pointer to an object as you would expect the destructor for the account class.

 

#include <iostream>

#include<string>

using namespace std;

 

class Player

{

private:

    string name;

    int health;

    int xp;

   

public:

    void set_name(string n)

    {

        name = n;

    }

    Player()  //constructor1

    {

        cout<<"No arguments constructor"<<endl;

    }

    Player(string n)  //constructor2

    {

        cout<<"One argument constructor"<<endl;

    }

    Player(string n,int health,int xp)  //constructor3

    {

        cout<<"Multiple arguments constructor"<<endl;

    }

    ~Player()

    {

        cout<<"Destructor is called for "<<name<<endl;

    }

};

 

int main()

{

    {

        Player anubhav;  //object anubhav is there within the scope,constructor1 is called

        anubhav.set_name("Anubhav");  //so when it goes out of scope the destructor will be called automatically 

    }  //as soon as the scope ends destructor is called automatically fo this particular scope  

   

    {

        Player abc;  //constructor1 will be called since no argument

        abc.set_name("abc");

        Player def("def");  //constructor2 is called

        def.set_name("def");

        Player ghi("ghi",10,2);  //constructor3 is called

        ghi.set_name("ghi");

    }  //destructor is called in the reverse order from ghi to abc, since they are in stack and last one is popped first

   

    Player *enemy = new Player;  //constructor1 is called

    enemy->set_name("Suman");

   

    Player *boss = new Player("Kumar",12,34);  //constructor3 is called

    boss->set_name("Kumar");

   

    delete enemy;  //to empty the space of heap

    delete boss;

    return 0;

}


Default Constructor:

Do not expect any arguments, if you write no constructor all for the class C++ will generate a default constructor that does nothing.

If you don't provide any constructor it will provide a system generated no args constructor

that will allow you to create objects.

If we created a constructor with some arguments(args) in main while the object is created then no more default constructor is called, since we created a a constructor with args.


#include <iostream>

#include<string>

using namespace std;

 

class Player

{

private:

    string name;

    int health;

    int xp;

   

public:

    void set_name(string n)

    {

        name = n;

    }

    string get_name()

    {

        return name;

    }

   

    Player(string n_val,int hel_val,int xp_val)

    {

        name = n_val;  //values are assigned not initialized

        health = h_val;

        xp = xp_val;

    }

};

int main()

{

    Player anubhav;  //it gives an error since it will call a default constructor with no args

    Player hero{"Anubhav",80,34};  //compiles well

    return 0;

}


Constructor Initialization List:

We want to do is have the member data values initialized to our values before the constructor body executes. We really want to initialize the data member to our data values as they're created that's what constructor initialization list allow us to do. Now we can be sure that our data members have been initialized to our own values before any code in the constructor body is executed. And they should be initialized in same order they have been declared.

 

#include <iostream>

#include<string>

using namespace std;

 

class Player

{

private:

    string name;

    int health;

    int xp;

   

public:

    Player();

    Player(string name_val);

    Player(string name_val,int health_val,int xp_val);

};

Player :: Player() //constructor initialization list

    :name{"none"},health{0},xp{0} { }//nothing in constructor body, values are initialized not assigned

Player :: Player(string name_val)

    :name{name_val},health{0},xp{0}{ }

Player :: Player(string name_val,int health_val,int xp_val)

    :name{name_val},health{health_val},xp{xp_val}{ }

int main()

{

    Player empty;

    Player arg1{"Anubhav"};

    Player agr3{"Suman",56,34};

    return 0;

}


Delegating Constructor:

Only works in constructor initialization list, to remove the code duplicity.

 

#include <iostream>

#include<string>

using namespace std;

 

class Player

{

private:

    string name;

    int health;

    int xp;

   

public:

    Player();

    Player(string name_val);

    Player(string name_val,int health_val,int xp_val);

};

Player :: Player(string name_val,int health_val,int xp_val)  //this constuctor will work as a delegating constructor

    : name{name_val},health{health_val},xp{xp_val} {cout<<"three arg constructor"<<endl;}

    //provide the values of all three arguments, it will work as a delegating constructor

 

Player :: Player() //constructor initialization list

    : Player{"none",0,0} {cout<<"no arg constructor"<<endl;}  //constructor body followed by, Player(3 arguments) is delegating constructor

 

Player :: Player(string name_val)

    : Player{name_val,0,0} {cout<<"one arg constructor"<<endl;}

   

int main()

{

    Player empty;

    Player arg1{"Anubhav"};

    Player agr3{"Suman",56,34};

    return 0;

}


Copy Constructor:

When objects are copied C++ must create a new object from an existing object. Copy of an object made when passing object by values as a parameter, returning an object from a function by value, constructing one object based on another of the same class.

If you don't provide your own way of copying object by the value then the compiler provides a default way of copying objects. If you are using pointer data member then the pointer is copied not the data it was pointing too.


#include <iostream>

#include<string>

using namespace std;

 

class Player

{

private:

    string name;

    int health;

    int xp;

   

public:

    string get_name(){ return name; }  //methods of class

    int get_health() { return health; }

    int get_xp() { return xp; }

   

    Player(string name_val = "None",int health_val = 0,int xp_val = 0); //default constructor

    Player(const Player &source); //copy constructor,  &source is an object that help us to copy

    ~Player() { cout<<"Destructor is called for: "<<name<<endl; }  //destructor

};

Player :: Player(string name_val,int health_val,int xp_val)

    : name{name_val},health{health_val},xp{xp_val} {cout<<"three arg constructor "+name<<endl;}

 

Player :: Player(const Player &source)  //copy constructor

    :name{source.name},health{source.health},xp{source.xp}{

        cout<<"Copy constructor - made copy of: "<<source.name<<endl;

    }

void display(Player p)  //function uses object 'p' to copy the value

{

    cout<<"Name: "<<p.get_name()<<endl;

    cout<<"Health: "<<p.get_health()<<endl;

    cout<<"Xp: "<<p.get_xp()<<endl;

}

int main()

{

    Player empty;

    display(empty);  //empty is an object, so a copy of empty is made and passed to the function

    Player obj1{"Anubhav",100,1};

    display(obj1);

    return 0;

}


Shallow Copying:

The copy constructor first will assume that the object we're copying has a raw pointer when the object is constructed it will likely allocate storage for the data of if that pointer is pointing to.

It is default behaviour of copy constructor, it basically does member wise copying of all the object attributes. So we end up with the newly created object and the object being copied both pointed to the same area of storage in the heap. Problem: When we release the storage in the destructor, the other object still refers to the released storage.


this Pointer:

this is a reserved keyword that contains the address of the current object, so it's a pointer to the object that's currently being used by the class member methods. All member access is done via the 'this' pointer.

The 'this' keyword can only be used within the scope of the class in many other object oriented programming languages we use 'self' like in python.

We can use it to explicitly access the data members and methods; we can also use it to determine if two objects are the same.

 

Using ‘const’ With Classes:

Pass arguments to class member methods as const, we can also create const objects.

#include <iostream>

#include<string>

using namespace std;

 

class Player

{

private:

    string name;

    int health;

    int xp;

   

public:

    string get_name() const  //to remove those error we added  const here

    {

        return name;

    }

    void set_name(string name_val)

    {

        name = name_val;

    }

    Player();

    Player(string name_val);

    Player(string name_val,int health_val,int xp_val);

};

Player :: Player()

    : Player{"none",0,0} { } 

Player :: Player(string name_val)

    : Player{name_val,0,0} { }

Player :: Player(string name_val,int health_val,int xp_val)

    : name{name_val},health{health_val},xp{xp_val} { }

 

void display_name(const Player &p)  //regular function

{

    cout<<p.get_name()<<endl;  //p.get_name is const so we will get error

}

int main()

{

    const Player object1{"Anubhav",14,23};

    Player object2{"Suman",23,4};

   

    object1.set_name("ABCD");  //error, since const keyword is used in object1

    cout<<object1.get_name()<<endl;  //error, get_name method could change the object1

    cout<<object2.get_name()<<endl; //compiles well, no const keyword

   

    display(object1); //error cz of const

    return 0;

}


Static Class Members:

Class data member can be declared as static, a single data member that belongs to the class not the objects, useful to store class-wide information.

Class function can be declared as static, independent of any objects, can be called using the class name.

When the function is static it can only access the static data members, it does not have the access to non-static members.

 

#include <iostream>

#include<string>

using namespace std;

 

class Player

{

private:

    static int num_players;  //does not belongs to any object it belongs to class

    string name;

    int health;

    int xp;

   

public:

    string get_name() {return name; }

    int get_health() { return health; }

    int get_xp() {return xp; }

   

    Player(string name_val,int health_val,int xp_val);

    Player(const Player &source);

    ~Player();

   

    static int get_num_players();  //func is static so only access static variables

};

int Player :: num_players{10}; //static variable is initialized

Player :: Player(string name_val,int health_val,int xp_val) 

    : name{name_val},health{health_val},xp{xp_val}

    {

        ++num_players;

    }

 

Player :: Player(const Player &source)

    : Player{source.name,source.health,source.xp} {} //3 args constructor is delegating constructor

   

Player :: ~Player()

{

    --num_players;

}

int Player :: get_num_players()

{

    return num_players;

}

void display()

{

    //that's going to return the integer that represents that static data member

    cout<<"Active players: "<<Player :: get_num_players<<endl;

}

int main()

{

    Player hero{"hero",0,0};

    display();

    return 0;

}


Structs(Structure) Vs Classes:

Struct is very much like classes, only difference is that the members of the struct are public by default whereas the member of a class is private.

 

struct Person

{

    string name;

}

int main()

{

    Person object;

    object.name = "Anubhav"; //no-error

}

 

struct:

1. Use struct for passive objects with public access.

2. Don't declare methods in struct

class:
1. Use class for active objects with private access

2. Implement getters/setters as needed

3. Implement member methods as needed.


Friends of Class:

A friend function of a class is defined outside that class scope but it has the right to access all private and protected members of the class. Even though the prototypes for friend functions appear in the class definition, friends are not member functions.


INHERITANCE:

Inheritance provides a method of creating new classes from existing classes, these new classes contain the data and the behaviors of the existing classes, allow us to reuse of existing classes, allow us to focus on the common attribute among a set of classes.

Allow new classes to modify behaviors of existing classes to make it unique without actually modifying the original class.


#include <iostream>

using namespace std;

 

class Account

{

public:

    string name{};

    double balance{};

    void deposit(double amount);

    void withdraw(double amount);

    void display();

    Account();

    ~Account();

};

 

Account :: Account()

    :name{"Anubhav"},balance{0.0}

    {

    }

Account :: ~Account()

{

}

void Account :: deposit(double amount)

{

    balance = amount;

    cout<<"Amount deposited: "<<amount<<endl;

}

void Account :: withdraw(double amount)

{

    cout<<"Amount withdrawn: "<<amount<<endl;

}

void Account :: display()

{

    cout<<"Name is Anubhav"<<endl;

    cout<<"Balance"<<endl;

}

 

class Saving_Account : public Account  //inherits all the public methods and attributes of parent class

{

public:

    double interest_rate{};

    Saving_Account();

    ~Saving_Account();

    void deposit(double amount);

    void withdraw(double amount);

};

 

Saving_Account :: Saving_Account()

    :interest_rate{3.0}

    {

    }

Saving_Account :: ~Saving_Account()

{

}

void Saving_Account :: deposit(double amount)

{

    cout<<"Amount deposited: "<<amount<<endl;

}

void Saving_Account :: withdraw(double amount)

{

    cout<<"Amount withdrawn: "<<amount<<endl;

}

 

int main()

{

    cout<<"=========Account class=========="<<endl;

    Account acc{};

    acc.deposit(1000);

    acc.withdraw(500);

   

    cout<<"========Saving Account class======="<<endl;

    //saving account object will have access to all the data mebers and methods of class

    Saving_Account sav_acc{};

    sav_acc.deposit(2000);

    sav_acc.display();  //methods has been accessed of parent class

   

    return 0;

}


Protected Member And Class Access:

Accessible from the base class itself and derived class, but not accessible by objects of base or derived class.


#include <iostream>

using namespace std;

 

class base

{

public:

    int a{};  

    void display()

    {

        cout<<"a :"<<a<<"b: "<<b<<"c: "<<c<<endl;

    }

protected:

    int b{};

private:

    int c{};

};

 

class Derived : public base

{

    void access_data_members()

    {

        a = 100;  //ok, can access since inherited

        b = 200;  //ok, can access since inherited

        c = 300;  //not accessible

    }

};

int main()

{

    Base base;

    base.a = 100;  //ok

    base.b = 200;  //error, objects acnnot access protected data member

    base.c = 300;  //error, objects acnnot access priavte data member

   

    Derived d;

    d.a = 100;  //ok

    d.b = 200;  //error

    d.c = 300;  //error

    return 0;

}


Constructor And Destructor:

Derived class inherits data and methods from its base class before C++ can initialize the derived object, it must initialize the base part of the derived object. So when the Derived class object is created first the base class constructor executes and then the derived constructor executes.

Class destructors are invoked in the reverse order as that of constructor, the derived part of the derived class must be destroyed before the base class destructor is invoked.

A derived class does not inherits:

1. Base class constructor

2. Base class destructor

3. Base class overloaded assignment operators

4. Base class friend functions

 

#include <iostream>

using namespace std;

 

class Base

{

private:

    int value{};

public:

    Base()

        :value{0}

        {

            cout<<"No args Base constructor"<<endl;

        }

    Base(int x)

        :value{x}

        {

            cout<<"One args Base constructor"<<endl;

        }

    ~Base()

    {

        cout<<"Base class destructor"<<endl;

    }

};

 

class Derived : public Base

{

private: 

    int double_value{};

public:

    Derived()

        :double_value{0}

        {

            cout<<"No args Derived constructor"<<endl;

        }

    Derived(int x)

        :double_value{x*2}

        {

            cout<<"One args Derived constructor"<<endl;

        }

    ~Derived()

    {

        cout<<"Derived class destructor"<<endl;

    }

};

 

int main()

{

    Base b;

    Derived d;

    return 0;

}


Passing Arguments To Base Class Constructor:

#include <iostream>

using namespace std;

 

class Base

{

private:

    int value{};

public:

    Base()

        :value{0}

        {

            cout<<"No args Base constructor"<<endl;

        }

    Base(int x)

        :value{x}

        {

            cout<<"One args Base constructor"<<endl;

        }

    ~Base()

    {

        cout<<"Base class destructor"<<endl;

    }

};

 

class Derived : public Base

{

private: 

    int double_value{};

public:

    Derived()

        :Base{}, double_value{0}  //Base class constructor has been called

        {

            cout<<"No args Derived constructor"<<endl;

        }

    Derived(int x)

        :Base{x}, double_value{x*2}  //Base class constructor has been called

        {

            cout<<"One args Derived constructor"<<endl;

        }

    ~Derived()

    {

        cout<<"Derived class destructor"<<endl;

    }

};

 

int main()

{

    Derived d;

    return 0;

}


Redefining Base Class Methods:

Derive class can directly invoke base class methods, derive class can override or redefine base class methods; it is very powerful in the context of polymorphism.

Static binding of method calls, binding of which methods to use is done at compile time, default binding for C++ is static

 

#include <iostream>

using namespace std;

 

class Account

{

protected:

    double balance{};

public:

    Account();

    Account(double b);

    void deposit(double amount);

    void withdraw(double amount);  

};

Account :: Account()

    :Account{0.0}  //delegating to one arg constructor

    {

    }

Account :: Account(double b)

    :balance(b)

    {

    }

void Account :: deposit(double amount)

{

    balance += amount;

    cout<<"Balance: "<<balance<<endl;

}

void Account :: withdraw(double amount)

{

    balance -= amount;

    cout<<"Balance: "<<balance<<endl;

}

 

class Saving_Account : public Account

{

private:

    double int_rate{};

public:

    Saving_Account();

    Saving_Account(double b,double i_r);

    void deposit(double amount); //method of base class has been overriden

    //withdraw method is also inherited

};

 

Saving_Account :: Saving_Account(double b,double i_r)

    :Account{balance}, int_rate{i_r}  //base class constructor has been called

    {

    }

Saving_Account :: Saving_Account()

    :Saving_Account{0.0,0.0}  //delegating the 2 args constructor

    {

    }

void Saving_Account :: deposit(double amount)

{

    amount = amount + (amount * int_rate/100);

    Account :: deposit(amount);  //base class method has been called

}

int main()

{

    Account a1{1000};

    a1.deposit(500);

    a1.withdraw(1000);

   

    Saving_Account s1{1000,5};  //balance is 1000 and interest rate is 5%

    s1.deposit(1000);

    s1.withdraw(200);

    return 0;

}


Multiple Inheritence:

A derive class inherits two or more base classes at the same time.



================================================================

Comments

Popular posts from this blog

SQL Course (PART-1)

PYTHON BASICS OF BEGINNER's (PART-2)

Open_CV BASICS