objects - entities in the real world class - blueprint of these entities
each object has properties (variables) and methods (functions)
Access Modifies:
- private - data and methods accessible inside class (default)
- public - data and methods accessible to everyone
- 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);
}- Encapsulation
- Abstraction
- Inheritance
- Polymorphism
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 - 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
}
}- Non-parametrized constructor - no parameters in the constructor function
- Parametrized constructor - parameters present in constructor function
- 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;
}A shallow copy of an object copies all of the member values from one object to another.
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.
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;
}- 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
}The capability of a class to derive properties and characteristics from another class is called Inheritance.
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;
}
};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
}Private, Protected, and Public inheritance.
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.
-
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; } };
-
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; } };
-
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; } };
-
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; } };
-
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 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 is achieved through function overloading and operator overloading. Constructor overloading is also a form of compile-time polymorphism.
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;
}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;
}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 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.
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 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 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
= 0to 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;
}
};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.
In Functions:
- A variable declared as
staticinside 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 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 calledExplanation:
- The static object
objinsidetestFunction()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.
kisi ko add krna ho toh bata dena :)