Skip to content

Instantly share code, notes, and snippets.

@ShardulJunagade
Last active June 16, 2026 04:22
Show Gist options
  • Select an option

  • Save ShardulJunagade/647b5e5ea2237b237890c7ef988a6a6a to your computer and use it in GitHub Desktop.

Select an option

Save ShardulJunagade/647b5e5ea2237b237890c7ef988a6a6a to your computer and use it in GitHub Desktop.
oops-notes

OOPs - Object Oriented Programming

Classes and Objects

objects - entities in the real world class - blueprint of these entities

each object has properties (variables) and methods (functions)

Access Modifies:

  1. private - data and methods accessible inside class (default)
  2. public - data and methods accessible to everyone
  3. protected - data and methods accessible inside class and to its derived class (used in inheritance)
class Teacher {
private:
	double salary;
	
public:
	// properties (attributes)
	string name;
	string dept;
	string subject;

	// methods (member functions)
	void changeDept(string newDept) {
		dept=newDept;
	}
	void setSalary(int s) {       // setter
		salary=s;
	}
	double getSalary() {          // getter
		return salary;
	}
};

int main() {
	Teacher t1;
	t1.name="Shardul";
	t1.dept="CS";
	t1.salary = 10000000;
	t1.setSalary(20000000);
}

Main terms of OOPs;

  1. Encapsulation
  2. Abstraction
  3. Inheritance
  4. Polymorphism

Encapsulation

Encapsulation is wrapping up of data and member functions in a single unit called class.

  • making a capsule of data and methods = creating a class
  • helps in data hiding (by giving private access modifier to some attribute)

Constructor

Constructor - special function invoked automatically at time of object creation, used for initializing a object.

  • always declared in public
  • same name as class
  • doesn't have a return type
  • only called once (automatically) , at object creation (cant be called manually)
  • memory is allocated when constructor is called
class Teacher {
public:
	Teacher() {         // constructor
		cout<< "I am a constructor,";
		dept="CSE";          // auto inits cse as department
	}
}

3 types of constructors:

  1. Non-parametrized constructor - no parameters in the constructor function
  2. Parametrized constructor - parameters present in constructor function
  3. Copy constructor - a constructor that creates a new object as a copy of an existing object. Teacher t1 = t2; will call the copy constructor if defined.

this is a pointer to the object being created, used to copy values and properties from one object to another. this->property is same as *(this).property

class Teacher {
public:
    string name;
    string dept;
    string subject;
    double salary;

    // Copy constructor
    Teacher(const Teacher &t) {      // passed as reference
        this->name = t.name;
        this->dept = t.dept;
        this->subject = t.subject;
        this->salary = t.salary;
        cout << "Copy constructor called for " << name << endl;
    }
};

int main() {
    Teacher t1("Shardul", "CSE", "OOPs", 20000000);
    Teacher t2(t1);    // Copy constructor called
    cout << "Name of t2: " << t2.name << endl;
}

Shallow Copy and Deep Copy

A shallow copy of an object copies all of the member values from one object to another.

Shallow Copy Example

class Student {
public:
    string name;
    double* cgpaPtr;
    Student(string name, double cgpa) {
        this->name = name;
        cgpaPtr = new double;
        *cgpaPtr = cgpa;
    }
    // Shallow copy constructor
    Student(const Student &obj) {
        this->name = obj.name;
        this->cgpaPtr = obj.cgpaPtr; // just copies the pointer
    }
    void getInfo() const {
        cout << "Name: " << name << endl;
        cout << "CGPA: " << *cgpaPtr << endl;
    }
};
int main() {
    Student s1("Alice", 3.8);
    Student s2(s1); // Shallow copy
    *s2.cgpaPtr = 3.9; // Changing CGPA of s2
    s1.getInfo();   // Alice, 3.9 (s1 also changed)
    s2.getInfo();   // Alice, 3.9
    return 0;
}

A deep copy, on the other hand, not only copies the member values but also makes copies of any dynamically allocated memory that the members point to.

Deep Copy Example

class Student {
public:
    string name;
    double* cgpaPtr;
    Student(string name, double cgpa) {
        this->name = name;
        cgpaPtr = new double;
        *cgpaPtr = cgpa;
    }
    Student(const Student &obj) {
        this->name = obj.name;
        this->cgpaPtr = new double;
        *cgpaPtr = *(obj.cgpaPtr);
    }
    double getInfo() const {
        cout << "Name: " << name << endl;
        cout << "CGPA: " << *cgpaPtr << endl;
    }
};
int main() {
    Student s1("Alice", 3.8);
    Student s2(s1); // Copy constructor called
    s1.getInfo();   // Alice, 3.8
    s2.getInfo();   // Alice, 3.8
    *s2.cgpaPtr = 3.9; // Changing CGPA of s2
    s1.getInfo();   // Alice, 3.8 (s1 remains unchanged)
    s2.getInfo();   // Alice, 3.9 (s2 changed)
    return 0;
}

Destructor

  • opposite of constructor
  • clears statically allocated memory (default destructor, when main() ends)
  • need to create a custom destructor to deallocate dynamically allocated memory.
  • custom destructor is declared by adding ~ before the class name and a parameter list.
  • A destructor is a special member function of a class that is called automatically when an object goes out of scope or is deleted

Note - Destructors cant be overloaded, only one destructor can be defined in a class.

class Teacher {
public:
    string name;
    string dept;
    string subject;
    double salary;
    // Destructor
    ~Teacher() {
        cout << "Destructor called for " << name << endl;
    }
};
int main() {
    Teacher t1("Shardul", "CSE", "OOPs", 20000000);
    // Destructor will be called automatically when t1 goes out of scope
}

Inheritance

The capability of a class to derive properties and characteristics from another class is called Inheritance.

Non-parametrized constructor

class Person {
public:
    string name;
    int age;
    Person() { // Non-parametrized constructor
        name = "Unknown";
        age = 0;
    }
};
class Student : public Person {
public:
    string major;
    double gpa;
    Student() { // Non-parametrized constructor
        major = "Undeclared";
        gpa = 0.0;
    }
    void displayInfo() {
        cout << "Name: " << name << ", Age: " << age << ", Major: " << major << ", GPA: " << gpa << endl;
    }
};

Parametrized constructor

class Person {
public:
    string name;
    int age;
    Person(string name, int age) {
        this->name = name;
        this->age = age;
    }
};

class Student : public Person {
public:
    string major;
    double gpa;
    Student(string name, int age, string major, double gpa) : Person(name, age) {
        this->major = major;
        this->gpa = gpa;
    }
    void displayInfo() {
        cout << "Name: " << name << ", Age: " << age << ", Major: " << major << ", GPA: " << gpa << endl;
    }
};
int main() {
    Student s1("Alice", 20, "Computer Science", 3.8);
    s1.displayInfo(); // Output: Name: Alice, Age: 20, Major: Computer Science, GPA: 3.8
}

Modes of Inheritance:

Private, Protected, and Public inheritance.

Inheritance Modes

Modes of Inheritance: Base to Derived Access

When a class is inherited, the access level of its members in the derived class depends on both the access specifier of the base class members and the mode of inheritance used.

Base Class Member Public Inheritance Protected Inheritance Private Inheritance
public public protected private
protected protected protected private
private Not inherited Not inherited Not inherited

Explanation:

  • Public Inheritance: Public and protected members of the base class retain their access levels in the derived class. Private members are not inherited.
  • Protected Inheritance: Both public and protected members of the base class become protected in the derived class. Private members are not inherited.
  • Private Inheritance: All inherited members (public and protected) become private in the derived class. Private members are not inherited.

This determines how the derived class and its objects can access the base class members.

Types of Inheritance

  1. Single Inheritance: A class inherits from one base class.

    class Base {
    public:
        void display() { cout << "Base class display" << endl; }
    };
    class Derived : public Base {
    public:
        void show() { cout << "Derived class show" << endl; }
    };
  2. Multi-level Inheritance: A class inherits from another derived class.

    class Base {
    public:
        void display() { cout << "Base class display" << endl; }
    };
    class Intermediate : public Base {
    public:
        void show() { cout << "Intermediate class show" << endl; }
    };
    class Derived : public Intermediate {
    public:
        void print() { cout << "Derived class print" << endl; }
    };
  3. Multiple Inheritance: A class inherits from multiple base classes.

    class Base1 {
    public:
         void display() { cout << "Base1 class display" << endl; }
    };
    class Base2 {
    public:
         void show() { cout << "Base2 class show" << endl; }
    };
    class Derived : public Base1, public Base2 {
    public:
         void print() { cout << "Derived class print" << endl; }
    };
  4. Hierarchical Inheritance: Multiple classes inherit from a single base class.

     class Base {
     public:
         void display() { cout << "Base class display" << endl; }
     };
     class Derived1 : public Base {
     public:
         void show() { cout << "Derived1 class show" << endl; }
     };
     class Derived2 : public Base {
     public:
         void print() { cout << "Derived2 class print" << endl; }
     };
  5. Hybrid Inheritance: A combination of two or more types of inheritance.

     class Base {
     public:
         void display() { cout << "Base class display" << endl; }
     };
     class Derived1 : public Base {
     public:
         void show() { cout << "Derived1 class show" << endl; }
     };
     class Derived2 : public Base {
     public:
         void print() { cout << "Derived2 class print" << endl; }
     };
     class Derived3 : public Derived1, public Derived2 {
     public:
         void final() { cout << "Derived3 class final" << endl; }
     };

Polymorphism

Polymorphism is the ability of a function or an object to take on different forms based on its context. It allows methods to do different things based on the object it is acting upon, even if they share the same name.

Compile Time Polymorphism

Compile-time polymorphism is achieved through function overloading and operator overloading. Constructor overloading is also a form of compile-time polymorphism.

1. Function Overloading

Function overloading allows multiple functions with the same name but different parameter or types to coexist in the same scope. The compiler determines which function to call based on the arguments passed.

class Print {
public:
    void display(int i) {
        cout << "Integer: " << i << endl;
    }
    void display(double d) {
        cout << "Double: " << d << endl;
    }
    void display(string s) {
        cout << "String: " << s << endl;
    }
};
int main() {
    Print obj;
    obj.display(5);
    obj.display(3.14);
    obj.display("Hello");
    return 0;
}

2. Operator Overloading

Operator overloading allows you to redefine the behavior of operators for user-defined types (classes). This enables you to use operators like +, -, *, etc., with objects of your classes.

class Complex {
public:
    float real;
    float imag;
    Complex(float r, float i) : real(r), imag(i) {}
    Complex operator+(const Complex& other) {
        return Complex(real + other.real, imag + other.imag);
    }
};
int main() {
    Complex c1(1.0, 2.0);
    Complex c2(3.0, 4.0);
    Complex c3 = c1 + c2; // Calls the overloaded + operator
    cout << "Result: " << c3.real << " + " << c3.imag << "i" << endl;
    return 0;
}

3. Constructor Overloading

Constructor overloading allows you to define multiple constructors with different parameter lists in the same class. The appropriate constructor is called based on the arguments provided during object creation.

class Box {
public:
    float length;
    float width;
    float height;
    // Constructor with one parameter
    Box(float side) {
        length = width = height = side;
    }
    // Constructor with three parameters
    Box(float l, float w, float h) {
        length = l;
        width = w;
        height = h;
    }
};
int main() {
    Box box1(5.0);        // Calls the first constructor
    Box box2(3.0, 4.0, 5.0); // Calls the second constructor
    return 0;
}

Run Time Polymorphism (Dynamic Polymorphism)

Run-time polymorphism is achieved through method overriding using virtual functions. It allows a derived class to provide a specific implementation of a method that is already defined in its base class.

Virtual Functions

A virtual function is a member function in the base class that you expect to be redefined in derived class.

Virtual functions allow you to achieve run-time polymorphism by enabling dynamic binding. When a base class declares a function as virtual, it tells the compiler to support late binding for that function.

class Base {
public:
    virtual void show() {
        cout << "Base class show" << endl;
    }
};
class Derived : public Base {
public:
    void show() override {
        cout << "Derived class show" << endl;
    }
};

Abstraction

Abstraction is the concept of hiding the complex unnecessary implementation details and showing only the essential features of an object. It allows you to focus on what an object does rather than how it does it.

Abstraction is done using access modifiers (private, public, protected) and abstract classes.

Abstract Classes

  • Abstract classes are used to provide a base class from which other important classes can be derived.
  • It cannot be instantiated directly and are meant to be inherited by other classes.
  • An abstract class contains at least one pure virtual function, which is declared by appending = 0 to the function declaration.
class Shape {       // abstract class
public:
    virtual void draw() = 0; // Pure virtual function
};
class Circle : public Shape {
public:
    void draw() override {
        cout << "Drawing Circle" << endl;
    }
};

Static Keyword

The static keyword in C++ is used to define static variables and functions. It has different meanings depending on where it is used.

Note - static variables or objects cant be in initialized twice in the same scope, they are initialized only once.

Static Variables

In Functions:

  • A variable declared as static inside a function retains its value between function calls and is initialized only once for the lifetime of the program.
    void counter() {
        static int count = 0; // Static variable
        count++;
        cout << "Count: " << count << endl;
    }
    int main() {
        counter(); // Output: Count: 1
        counter(); // Output: Count: 2
        counter(); // Output: Count: 3
        return 0;
    }

In Classes:

  • A static member variable is shared among all instances of the class. It is not tied to any specific object and can be accessed using the class name.

    class Box {
    public:
        static int count; // Static variable
        Box() {
            count++; // Increment count for each object created
        }
        static void displayCount() {
            cout << "Number of Box objects created: " << count << endl;
        }
    };
    int Box::count = 0; // Initialize static variable
    int main() {
        Box b1, b2, b3; // Three Box objects created
        Box::displayCount(); // Output: Number of Box objects created: 3
        return 0;
    }

Static Objects

Static objects are objects that are created with the static keyword, which means they have a static storage duration. They are initialized only once and exist for the lifetime of the program.

class Demo {
public:
    Demo() {
        cout << "Constructor called" << endl;
    }
    ~Demo() {
        cout << "Destructor called" << endl;
    }
    void show() {
        cout << "Demo object function" << endl;
    }
};
void testFunction() {
    static Demo obj; // Static object
    obj.show();
}
int main() {
    testFunction();
    testFunction();
    // Destructor for static object will be called at program end
    return 0;
}

Output:
Constructor called
Demo object function
Demo object function
Destructor called

Explanation:

  • The static object obj inside testFunction() is created only once, and its constructor is called only the first time the function is executed.
  • The object persists for the lifetime of the program, and its destructor is called automatically when the program ends.

Extra topics - Friend Functions, Friend Classes

kisi ko add krna ho toh bata dena :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment