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
Post a Comment