What is Object-Oriented Programming?
#include <iostream> //preprocessor directive
using namespace std; //namespace directive
class Student {
private:
int age; //private data member
public:
void setAge(int a) { //Setter function
age = a; //Assigning new value to age variable
}
int getAge() { //Getter function
return age;
}
};
int main() {
Student student1; //Object creation
student1.setAge(45); //Calling setAge()
cout << "Age: "<< student1.getAge();
return 0;
}
Note:In the example above, age is a private attribute and cannot be accessed directly by the main function. Therefore, we use indirect access through the setAge function to assign a new value and the getAge function to retrieve it.
Inheritance: is the ability of a class to acquire properties and behaviors from another class. It helps hide complex implementation details by exposing only the essential features of an object, thereby reducing complexity and improving code readability.
In C++, the inheritance mode controls the accessibility of base class members within the derived class.
-
Public Inheritance Mode:
all public members of the base class remain public in the
derived class, and all protected members remain protected in the derived class.
Syntax:class Derived : public Base
Example#include <iostream> #include <string> class Animal { // Base class public: std::string name = "animal"; void sound() { std::cout << "Animal makes a sound"; } }; class Dog : public Animal { // Derived class with public inheritance public: void Detail() { std::cout<< "Name: "<<name; // Accessible as public sound(); // public method accessible } }; int main() { Dog dog; dog.Detail(); return 0; }
- Protected Inheritance: In protected inheritance, all public and protected members of the base class become protected members in the derived class. This means:
- Members that were public in the base class are no longer publicly accessible through the derived class; instead, they become protected.
- Protected members of the base class remain protected.
- Private members of the base class are not directly inherited.
- The derived class and its subclasses can access these members, but they are hidden from outside code when accessed through the derived class.
class Derived : protected Base
Example#include <iostream> using namespace std; class Father { public: char letter = 'a'; protected: int age = 12; private: char grade = 'A'; }; class Child : protected Father { public: void Members() { cout << "letter(now protected): " << letter; // accessible here cout << "age: " << age; // accessible here // cout << "grade: " << grade; // NOT accessible, will cause error } }; int main() { Child c; c.Members(); return 0; }
-
Private Inheritance:
both public and protected members of the base class become private in the derived class.
Private inheritance is the default mode if no access specifier is provided.
Syntax:class Derived : private Base
Example → Private Inheritance#include<iostream> using namespace std; class Father { public: char letter = 'a'; protected: int age = 12; private: char grade = 'A'; }; class Child : private Father { //private inheritance public: void Members() { cout<<"letter (now private): "<<letter; // accessible here cout<<"age (now private): "<<age; // accessible here // cout<<"grade: "<< grade; // NOT accessible (private in Father) } }; int main() { Child c; c.Members(); // cout<< c.letter; // Error // cout<< c.age; // Error return 0; }
Depending on how a derived class interacts with its base class(es), inheritance can be categorized into the following types: Single, Multilevel, Multiple, Hierarchical, and Hybrid inheritance.
- Single Inheritance: is a type of inheritance where a derived class inherits from only one base class. This is the simplest basic form of inheritance and assists in reusing the code of a single base class.
Example#include<iostream> using namespace std; class Person { // Base class public: string name; void displayInfo() { cout <<"Name: "<<name; } }; class Student : public Person { // Derived class public: int id; void displayStudent() { displayInfo(); // Call base class method cout<<"ID: "<<id; } }; int main() { Student s; s.name = "Yilma"; s.id = 12; s.displayStudent(); return 0; }
- Multiple Inheritance: when a derived class inherits from two or more base classes, this is known as multiple inheritance. As a result, features from several unrelated classes can be combined and reused by the derived class.
Example#include<iostream> using namespace std; class Teacher { public: void teach() { cout<<"Teaching."; } }; class Person { public: void walk() { cout<< "Walking."; } }; class Student : public Teacher, public Person { public: void introduce() { cout<< "I am a Student."; } }; int main() { Student s; s.introduce(); s.teach(); // from Teacher class s.walk(); // from Person class return 0; }
- Multilevel Inheritance: There is a chain of inheritance where a derived class
is created from another derived class, which in turn is derived from a base class.
Example#include<iostream> using namespace std; class Person { public: string name; void displayName() { cout<<"Name:"<<name; } }; class Student : public Person { public: int id; void displayId() { cout<< "ID: "<<id; } }; class Child : public Student { public: int age; void displayAge() { cout<<"Age: "<<age; } }; int main() { Child c; c.name = "Hermon"; c.id = 11; c.age = 3; c.displayName(); // from Person c.displayId(); // from Student c.displayAge(); // from Child return 0; }
- Hierarchical Inheritance: A single base class is the ancestor of several derived classes in hierarchical inheritance.
Example
#include<iostream> using namespace std; class GrandFather { public: void Origin() { cout<<"I am GrandFather."<<endl; } }; class Father : public GrandFather { public: void fatherInfo() { cout<<"I am Father."<<endl; } }; class Uncle : public GrandFather { public: void uncleInfo() { cout<< "I am Uncle."<<endl; } }; int main() { Father f; Uncle u; f.Origin(); // Inherited from GrandFather f.fatherInfo(); // Own method u.Origin(); // Inherited from GrandFather u.uncleInfo(); // Own method return 0; }
- Hybrid Inheritance: Combining two or more inheritance types—such as single, multilevel, multiple, and hierarchical into a single program is known as hybrid inheritance.It is applied when there are intricate relationships between classes.
#include <iostream> // preprocessor directive
using namespace std; // namespace directive
class Person { // Base class
public:
int id; // public variable
void displayId() { // public function
cout << "Person ID: " << id;
}
};
class Student : public Person { // public inheritance mode
public:
void showId() { // public function
cout << "Student ID: " << id;
}
};
int main() {
Student student1; // Object creation
student1.id = 7; // value
student1.showId(); // calling child function
student1.displayId(); // calling a parent (or base) class function using a derived class object
return 0;
}
Abstraction : is the process of displaying only the essential components of an object or function while concealing intricate implementation details is known as abstraction. This simplifies code, makes it easier to manage, and frees up the programmer to concentrate on the function of an object rather than its implementation.
For instance,Consider operation of a vehicle, you operate the steering wheel and pedals without fully understanding how the engine or braking system operate internally.
In C++, abstraction can be implemented in two main ways:
- By using abstract classes with pure virtual functions: a class is considered abstract if it has at least one pure virtual function and cannot be instantiated. Without putting all the methods into practice, these are utilized to define a broad interface. The particular implementation is provided via derived classes.
#include <iostream> // preprocessor directive
using namespace std;
class Shape { // Abstract class
public:
virtual void draw() = 0; // Pure virtual function
};
class Circle : public Shape {
public:
void draw() override {
cout << "Circle";
}
};
int main() {
Shape* s = new Circle();
s->draw(); // Output: Circle
delete s;
return 0;
}
Note: In the above example, the Shape class defines the action that a shape should perform drawing without detailing how it should be done. The Circle class takes care of the specific drawing behavior. Thanks to abstraction, the user simply calls draw() without needing to understand or manage the internal drawing mechanism.
By using access specifiers: Access specifiers in C++ allow you to expose or limit specific areas of a class. This limits the data and functions that are available and conceals the implementation details from class users.
#include <iostream> // preprocessor directive
using namespace std;
class Goat {
private:
int age; // Hidden from outside
public:
void setAge(int a) {
age = a;
}
double getAge() {
return age;
}
};
int main() {
Goat g;
g.setAge(12);
cout << "Age: " << g.getAge(); // Output: Age: 12
return 0;
}
Polymorphism is a fundamental concept in object-oriented programming that allows objects of different classes to be treated as objects of a common base class. It literally means “many forms,” and it allows the same function or method call to behave differently based on the object that is calling it.
Polymorphism provides flexibility in code design, enabling programmers to write more generic, reusable, and maintainable code. It allows you to use a single interface or function to handle different types of behavior depending on the object’s class.
This dynamic behavior enhances flexibility, scalability, and code reusability in object-oriented programming. As a fundamental pillar of OOP, polymorphism plays a key role in designing modular, maintainable, and extensible systems.
Polymorphism in C++ can be categorized as:
Compile-Time Polymorphism
-
Compile-Time Polymorphism is fixed while the program is being compiled and makes it possible to execute several jobs using the same function name depending on the function signature, which includes the kind or quantity of arguments given. By permitting several iterations of a function to live within the same scope, this type of polymorphism improves the readability and organization of code.
Compile-time polymorphism in C++ is mostly accomplished byoperator overloading
, which enables programmers to modify the behavior of operators (such as +, -, etc.) for user-defined types, andfunction overloading
, which enables numerous functions to have the same name but different parameter lists.
#include <iostream>
using namespace std;
class Num {
private:
int value;
public:
Num(int val) { // Constructor
value = val;
}
Num operator - () { // Overload unary '-' operator
return Num(-value);
}
void display() {
cout<<"Value: "<<value;
}
};
int main() {
Num n1(1); //object
Num n2 = -n1; // Calls overloaded '-' operator which is n1.operator-();
n2.display(); // Output: Value: -1
return 0;
}
Runtime Polymorphism
-
In order to enable a derived class to offer its own unique implementation of a function that is already specified in its base class, runtime polymorphism is resolved during program execution. Depending on their true type at runtime, objects can react differently to the same function call thanks to this dynamic behavior. Mechanisms like
inheritance, virtual functions, and function overriding
enable runtime polymorphism in C++, enabling the program to choose which function implementation to call while it is operating.
#include <iostream>
#include <string>
using namespace std;
class Department {
public:
virtual void manage() {
cout<<"The department can manage.";
}
};
class InformationSystem : public Department {
public:
void manage() override {
cout<<"Information System can manage.";
}
};
class ComputerScience : public Department {
public:
void manage() override {
cout<<"Computer Science can manage.";
}
};
int main() {
Department* is = new InformationSystem();
Department* cs = new ComputerScience();
is->manage();
cs->manage();
delete is;
delete cs;
return 0;
}
In the above example, the derived classes InformationSystems and ComputerScience
override the manage()
function from the Department class and execute their own specific tasks.