memory is allocated in stack (smaller is size fixed)
- stack memory stores local vairable and functions parameters
- memory allocated in stack is automatically deallocate
class Animal {
private :
int age;
public:
int sex;
string name;
void eat(){
cout<<"eat";
}
void sleep(){
cout<<"Sleep";
}
void getAge(){
return age;
}
void setAge(int age){
//same name of local and class variable then use this
this->age = age;
}
}
int main(){
//static memory allocation
Animal Dog;
cout<<dog.sex;
//cout<<dog.age not accessible
dog.setAge(10);
cout<<dog.getAge()
Dog.eat();
}
memory is allocated in heap (memory is large)
- allocation using new.
- manually need to deallocate memory using delete.
class Animal {
private :
int age;
public:
int sex;
string name;
void eat(){
cout<<"eat";
}
void sleep(){
cout<<"Sleep";
}
void getAge(){
return age;
}
void setAge(int age){
//same name of local and class variable then use this
this->age = age;
// this is pointer to current object
}
}
int main(){
//static memory allocation
Animal * Dog = new Animal;
cout<<(*dog).sex;
//above state can be access as this also
cout<<dog->sex;
//cout<<dog.age not accessible
dog->setAge(10);
cout<<dog->getAge()
Dog->eat();
}
int * arr = new int[n];
delete [] arr;
// Memory allocation
int **arr = new int* [row];
for(int i=0;i<row;i++){
arr[i] = new int[col];
}
//memory deallocation
for(int i=0;i<row;i++){
delete [] arr[i];
}
delete [] arr;
- function to initialize object properties
- no return value
class Animal{
private:
int age;
int y;
public:
//Default constructor
Animal(){
cout<<"Constructor called";
this->age = 0;
this->y = 0;
}
//Parameterized constructor with default argument
Animal(int age,int y = 10){
cout<<"Constructor called";
this->age = age;
this->y = y;
}
//copy constructor
Animal(Animal & obj){
this->age = obj.age;
this->y = obj.y;
}
/* Note */
/* Pass by reference in copy constructor because if pass by value then agin obj is copy of parameters and infinite loop is created. */
}
//Driver call
Animal a; // default constructor call
Animal *ptr = new Animal //default constructor call
Animal b(10,30); //parameterized constructor call
Animal b(10); // y value by default will become 10
Animal *ptr2 = new Animal(10); //parameterized constructor call
//copy constructor call
Animal c = a;
Animal d(a);
Animal e(*ptr);
- for static allocation automatically memory is free
- for dynamic allocatio memory release necessary
class Animal {
private :
int age;
public:
int sex;
string name;
void eat(){
cout<<"eat";
}
void sleep(){
cout<<"Sleep";
}
void getAge(){
return age;
}
void setAge(int age){
//same name of local and class variable then use this
this->age = age;
// this is pointer to current object
}
Animal(){
cout<<"constructor";
this->age = 0;
this->sex = 0;
}
~Animal(){
cout<<"Deconstructor called";
}
}
int main(){
Animal a;
a.sex = 0;
return 0;
//for destructor will be by default call automatically
Animal *b = new Animal();
b->sex = 0;
return 0;
//here desctructor for b will be not called so need to use delete for call destructor
delete b;
return 0;
}
- Encapsulation
- Inheritance
- polymorphism
- Abstraction
- Data hiding
- security
- perfect encapsulation all data members are private
Base class / parent class / super class -> [Inheritance] -> subclass / child class / derived class
code syntax
class child : ____ Parent
//here Parent is parent class
// ___ is where mode of inheritance comes which are
// public
// private
// protected
class Animal {
public:
int age;
};
class Dog : public Animal {
};
int main(){
Dog d1;
cout<<<d1.age;
return 0;
}
Base class access modifier | modes/ types of inheritance
|
|||
---|---|---|---|---|
public |
|
|||
protected |
|
|||
private |
|
- single
- multilevel
- mulitiple
- Hierarchical
- Hybrid
In case of mulitple there is case of ambiguity to resolve it we have to use scopre resolution
class A{
public:
int a;
};
class B{
public:
int a;
};
class C: public A,public B {
//here a is ambiguis so to access when object is created scope reslution is required
};
int main(){
C obj;
cout<<obj.A::a<<" "<<obj.B::a;
return 0;
}
existing in many forms, there are two types
- compile time polymorphism
- function overloading
- operator overloading
- runtime polymorphism
- function overriding
functional overloading
class Maths{
public:
int sum(int a,int b){
return a+b;
}
int sum(int a,int b,int c){
return a+b+c;
}
float sum(int a,float b){
return a+b;
}
}
int main(){
Maths m;
cout<<m.sum(1,2); //3
cout<<m.sum(1,2,3) //6
cout<<m.sum(1,3.5f) //4.5
return 0;
}
Operator overloading
can make operator to do things which they are not intendent to do.
+ is binary operator so o1 is calling onject and o2 is passed as argument object
class Param {
public:
int val;
int operator + (Param &obj){
int v1=this->val;
int v2 = obj->val
return v1+v2;
}
}
int main(){
Param o1,o2;
o1.val =1;
o2.val = 4;
cout<<o1+o2; //5
}
Function overriding - same function definition but different definition
class Animal {
public:
void speak(){
cout<<"speaking";
}
};
class Dog : public Animal {
public:
void speak() {
cout<<"Barking"
}
};
int main(){
Animal a;
a.speak(); //speaking
Dog b;
b.speak(); //barking
//upcasting
Animal* c = new Dog()
c->speak(); //will call parents function //speaking
//Downcasting
Dog * d = new Animal();
Dog * d = (Dog *) new Animal();
d.speak();
}
class Animal {
public :
virtual void speak(){
cout<<"speak";
}
};
class Dog: public Animal {
public:
void speak(){
cout<<"Barking";
}
};
int main() {
Animal* a= new Dog();
a->speak(); //Barking
//due to use of virtual object data pointer function is called not the assigned variable pointer
}
default behaviour of compiler function are called of two pointer assigned if we use virtual function the dynamic created object function are called.
Animal * a = new Animal();
//only Animal constructor is called
Dog * a = new Dog();
// Animal constructor called and then Dog constructor called
//upcasting
Animal * a = new Dog();
//Animal 1st and Dog 2nd constructor called
Dog * a = new Animal();
//Animal constructor called only
Implementation hiding Encapsulation is a subset of Abstraction It can be achieved with the help of Encapsulation and access modifier.
- Abstraction - essential info show
- Encapsulation - wrapping data member and member function using class
The const keyword to declare that a variable, function, or object is immutable, i.e., its value cannot be changed after initialisation.
E.g., if you declare a variable as const int x = 5;, you cannot modify the value of x later in the program any attempt to modify the value will result in a compilation error.
You can declare a function as const, which means that it does not modify the state of the object it is called on.
Compiler may be able to store const variables in read-only memory, which can result in faster access time.
int main(){
const int x = 5;
//Intialization can be done
// but we cannot re-assigne value
x = 10;//error
//const with pointer
int *a = new int;
*a = 2;
cout<<*a; //2
delete a;
int b = 5;
a=&b;
cout<<*a; //5
const int *a = new int(2);
//and
int const *b = new int(5);
//both syntax are same because
//If I write const before * then content will be const
// this mean data/content of pointer is const
// but pointer can be reassigned
cout<<*a; //2
*a = 20; //will throw compilation error
//this will not throw error
int b = 5;
a = &b;
cout<<*a; //valid
// const pointer but non const data
int * const a = new int(2);
//this mean data/content can be changed but
// pointer can not be reassigned
cout<<*a; // 2;
*a = 20; //valid
cout<<*a; //20
int b = 30;
a = &b; //will throw error
//const pointer and const data
const int * const a = new int(50);
*a = 50; //error
a = &b; //error
return 0;
}
If method of class is made const then it means that method can't change any of the member variable
class abc{
int x;
int *y;
public:
abc(){
x = 0;
y = new int(0);
}
int getX() const{ //this mean it can never modify any variable of class
x = 50; // error
return x;
}
void setX(int _val){ //setter function can never contain const
x =_val;
}
int getY(){
return y;
}
void setY(int _val){
*y = _val;
}
};
void printAB(const abc &a ){ // now that that we have marked our argument variable as const
//this print function cannot modify any variable of a object
cout<<a.getX(); //valid
cout<<a.getY(); //will throw error because this function is not const, mean it is possible that this function might change the variables of a object
cout<<a.setX(); //will throw error as function modifying variable of a object
}
int main(){
abc a;
cout<<a.getX();
cout<<a.getY();
return 0;
}
class abc{
mutable int x;
int *y;
public:
abc(){
x = 0;
y = new int(0);
}
int getX() const{ //this mean it can never modify any variable of class
x = 50; // will not throw error because we have made it mutable
return x;
}
};
class abc{
mutable int x;
int *y;
const int z;
public:
//old ctor method (ctor - constructor)
// abc(int _x,int _y,int _Z){
// x = _x;
// y = new int(_y);
// z = _z; //will throw error because z is const and reassigning value
// }
// better way of writing ctor
abc(int _x,int _y):x(_x), y(new int(_y)), z(_z){
// z can be reassign in this method.
// normal constructor code can also be written
cout<<"constructor called";
x = _x * 10; // valid
z = _z * 10; //not valid, reassigne only possible in initialization list
}
int getX() const{ //this mean it can never modify any variable of class
x = 50; // will not throw error because we have made it mutable
return x;
}
};
In C++, macros are preprocessor directives that allow you to define constants, functions, or code snippets that can be used throughout your code. They are typically defined using the #define directive and are evaluated by the preprocessor before the code is compiled.
Macros can be used for a variety of purposes, such as defining constants or creating shorthand for commonly used expressions.
#include <iostream>
using namespace std;
#defing PI 3.145954
#defint MAX(x,y) (x>y ? x:y)
float circleArea(float r){
return PI * r * r;
}
int main(){
cout<< circleArea(3.2);
cout<<MAX(3,5)
int a = 0;
int b = -1;
cout<<MAX(a,b) // (0>-1 ? 0 : -1) // expansion
return 0;
}
class abc{
public:
int x;
int *y;
abc(int _x,int _y):x(_x),y(new int(_y)) {}
//default dumb copy constructor
// it does shallow copy
// abc(const abc & obj){
// x = obj.x;
// y = obj.y;
// }
//deep copy for copy constructor
abc(const abc & obj){
x = obj.x;
y = new int(*obj.y);
}
void print() {
cout<<x<<y<<*y;
}
~abc(){
delete y;
}
};
int main(){
abc a(1,3);
a.print();
abc b(a);
b.print();
// here content of b is exactly same as a event pointer y points to same address as address of y in object a, now both are linked if obj a changes y pointer value
// it will also affect obj b
b.*y = 20;
a.print(); // y = 20;
b.print(); // y = 20
return 0;
}
- global variable
- written outside a function
- accessible to all function same copy
- local variable
- written inside a function
- accessible inside that function scope only
- scoped
#include <iostream>
using namespace std;
//global variable
int x = 2;
int main(){
// x = 20; //will access global variable and modify
// ::x = 20; same as above if x not declared before it will take reference of globar variable
int x = 20;
cout<<x; //20 - will access local first preference higher , if not found then it will look for global scope
//if want to access global variable explicitly
cout<<::x; //2
return 0;
}
static data member - that variable is going to share memory with all of the class instances
static member function - there is no instance of that class being passed into that method
class abc {
public:
int x,y;
void print() const {
cout<<x<<y;
}
};
int main(){
abc a = {1,2};
abc b = {2,5};
a.print(); // 1 2
b.print(); // 2 5
}
Static data member
class abc{
public:
static int x,y;
void print() const {
cout<<x<<y;
}
};
// need to define class static variables outside class to use them
int abc::x;
int abc::y;
int main(){
abc a;
a.x = 10;
b.y = 5;
a.print(); // 10 5
abc b;
b.x = 15;
b.y = 20;
a.print(); //15 20 --- will modify for class a as well as static member is shared among all class instances
b.print(); //15 20
return 0;
}
Static member function
class abc{
public:
static int x,y;
static void print() const { // function can only use static data members as It does not know instance data members
cout<<x<<y;
}
};
// need to define class static variables outside class to use them
int abc::x;
int abc::y;
int main(){
abc a;
a.x = 10;
b.y = 5;
abc::print(); // 10 5
return 0;
}
- Delivering only essential information to the outer world while masking the background details.
- It is a design and programming method that separates the interface from the implementation.
- Real life e.g., various functionalities of AirPods but don't know the actual implementation/working
- To drive a car, one only needs to know the driving process and not the mechanics of the car engine
Abstraction in Header files
- Function's implementation is hidden in header files.
- We could use the same program without knowing its inside working.
- E.g., Sort(), for example, is used to sort an array, a list, or a collection of items, and we know that if we give a container to sort, it will sort it, but we don't know which sorting algorithm it uses to sort that container.
Abstraction using Classes
- Grouping data members and member functions into classes using access specifiers.
- A class can choose which data members are visible to the outside world and which are hidden.
What is Abstract Class?
- Class that contains at least one pure virtual function, and these classes cannot be instantiated.
- It has come from the idea of Abstraction.
Bird.h implementation
#if !define(BIRD_H)
#define BIRD_H
#include <iostream>
class Bird{
public:
virtual void eat() = 0;
virtual void fly() = 0;
//classes that inherits this class
// has to implement pure virutal functions
};
class sparrow: public Bird{
private:
void eat(){
std::cout<<"sparrow is eating";
}
void fly(){
std::cout<<"sparrow is flying";
}
};
class eagle: public Bird{
private:
void eat(){
std::cout<<"eagle is eating";
}
void fly(){
std::cout<<"eagle is flying";
}
};
#endif //BIRD_H
main file implementation
#include <iostream>
#include "bird.h"
using namespace std;
void bridDoesSomething(Bird *&b){
b->eat();
b->fly();
}
int main() {
Bird *bird1 = new sparrow();
birdDoesSomething(bird1); //here sparrow related functions will be called
Bird *bird1 = new eagle();
birdDoesSomething(bird2); // here eagle related functions will be called
Bird *b = new Bird(); //cannot instantiate Bird interface object, other class which inherits this interface their class can be instantiate
return 0;
}
Design Strategy
-
Abstraction divides code into two categories:interface and implementation. so when creating your component, keep the interface separate from the implementation(Bird.h file) so that if underlying implementation changes, the interface stays the same(main file)
-
In this instance, any program that uses these interfaces would remain unaffected and would require recompilation with the most recent implementation.
to reduce function call overhead
It is used when function is smaller, because if used in bigger function then executable size will increase as the code size will increase as code will be copied and pasted in where called
- An inline function is a regular function that is defined by the inline keyword.
- The code for an inline function is inserted directly into the code of the calling function by compile while compiling, which can result in faster execution and less overhead compared to regular function calls.
- Instead of calling function the statements of functions are pasted in calling function.
- Used with small sized functions. So that executables are small (handled automatically by compiler optimisation levels).
#include <iostream>
inline void numberShow(int num){
cout<<num;
} //this way we don't need to edit all the functional calls and also it reduces function call overhead
int main(){
numberShow(10);
numberShow(20);
return 0;
}
- friend is a keyword in C++ that is used to share the information of a class that was previously hidden.
- For example, the private members of a class are hidden from every other class and cannot be modified except through getters or setters. Similarly, the protected members are hidden from all classes other than its children classes.
#include<iostream>
using namespace std;
class A{
private:
int x;
public:
A(int _x):x(_x){};
int getX() const {
return x;
}
int setX(int _x){
x = _x;
}
//by using friend keyword allowing to access private member of this class
friend class B;
friend void print(const A&a);
};
class B{
public:
void print(const A& a){
cout<<a.x; //valid
}
};
void print(const A &a){
cout<<a.x;
}
int main(){
A a(5);
B b;
b.print(a);
print(a);
return 0;
}
- Although information hiding is encouraged because it prevents unwanted bugs in a program, there are cases where two classes need to frequently interact with each other.
- In such a scenario, one of the classes declares another to be its friend.
- The class declaring the friendship can now make all of its data available to its friend
used when we don't want user to create that class instance and keep track of instance of that class
#include <iostream>
using namespace std;
class Box {
int w;
Box(_w):w(_w){};
public:
int getW() const{ return w;}
void setW(int _w) {
w = _w;
}
friend class BoxFactory;
}
class BoxFactory{
int count;
public:
Box getABox(int _w){
++count
return new Box(_w);
}
}
int main(){
BoxFactory bfact;
Box b = bfact.getABox(5);
Box b = new Box(5); //not possible
cout<<b.getW();
return 0;
}
- way to achive runtime polymorphism
How Virtual works
- VTables Formed for every class having at least one virtual function and for its derived classes.
- It is static arrays, hence one instance for a class.
- VPtr (a hidden member pointer) is added by compiler to classes with virtual and its derived classes.
- Depending upon the object type VPtr is bonded to a VTable.
VTabIes are created at compile time. When object of a particular type is created at runtime. There will be a VPtr which will be initialised to point to a static VTable at the time of construction.
NO. Constructor cannot be virtual, because when constructor of a class is executed there is no virtual table in the memory, means no virtual pointer defined yet. So, the constructor should always be non-virtual. A virtual call is a mechanism to get work given partial information. In particular virtual allows us to call a function knowing only any Interfaces and not the exact type of object. To create an object you need complete information. In particular, you need to know the exact type of what you want to create. Consequently, a "call to a constructor" cannot be virtual.
- Yes.
- It is really important to handle proper destruction of Derived classes.
class Base {
public:
Base(){
cout<<"base ctor";
} //ctor cannot be virtual because if object is not created virtual table will not be created.
virtual ~Base(){
cout<<"Base dtor";
} // dtor should be virtual this implies that derived class from base after code completetion will first call derived destructor as base class vptr points to and then Base class dtor will be called
};
class Derived: public Base {
int *a;
public:
Derived(){
a = new int[20];
cout<<"Derived ctor";
}
~Derived() {
delete[] a;
cout<<"Derived dtor";
}
};
int main() {
Base *b = new Derived();
return 0;
}
// OUTPUT-
// Base ctor
// Derived ctor
// Derived dtor
// Base dtor