Inheritance
Inheritance is a fundamental concept in OOP that allows you to define a new class based on an existing one. The new class (called the derived class or child class) inherits the properties and methods of the base class (or parent class).
#include <iostream>
using namespace std;
class Animal { // Base class
public:
void eat() {
cout << "I can eat!" << endl;
}
};
class Dog : public Animal { // Derived class
public:
void bark() {
cout << "I can bark! Woof!" << endl;
}
};
int main() {
Dog dog;
dog.eat(); // Inherited from Animal
dog.bark(); // Specific to Dog
return 0;
}
Abstraction
Abstraction involves creating simple, abstract models of more complex systems by encapsulating the complex details and exposing only what's necessary. In C++, we can use classes and interfaces for abstraction.
Consider an example of a simple Computer class:
#include <iostream>
using namespace std;
class Computer {
public:
void PowerOn() {
cout << "Computer is turned on!" << endl;
}
void PowerOff() {
cout << "Computer is turned off!" << endl;
}
};
int main() {
Computer myPC;
myPC.PowerOn();
myPC.PowerOff();
return 0;
}
Here, the internal details of how a computer turns on or off are hidden. We just need to know about PowerOn() and PowerOff() methods.
Polymorphism
Polymorphism is the ability of an object to take on many forms. The most common use of polymorphism in OOP occurs when a parent class reference is used to refer to a child class object.
#include <iostream>
using namespace std;
class Animal { // Base class
public:
virtual void sound() {
cout << "Animals make sounds" << endl;
}
};
class Dog : public Animal { // Derived class
public:
void sound() {
cout << "Dogs bark!" << endl;
}
};
int main() {
Animal* animal = new Dog();
animal->sound(); // Outputs: Dogs bark!
delete animal;
return 0;
}
Here, sound() function in Dog class is overriding the sound() function of Animal class. This demonstrates polymorphism as we use a base class pointer to refer to a derived class object and call the correct version of sound().
Encapsulation
Encapsulation is an Object-Oriented Programming principle that binds together the data and functions that manipulate the data, and keeps them safe from outside interference and misuse. In simpler words, it is the wrapping up of data and functions into a single unit called class.
Here's a simple example using a BankAccount class:
#include <iostream>
using namespace std;
class BankAccount {
private:
double balance; // Private attribute
public:
BankAccount(double bal) {
if (bal > 0)
balance = bal;
else
balance = 0.0;
}
void deposit(double amount) {
balance += amount;
}
void withdraw(double amount) {
if (balance - amount >= 0)
balance -= amount;
else
cout << "Insufficient balance!" << endl;
}
double getBalance() {
return balance;
}
};
int main() {
BankAccount account(100.0);
account.deposit(50.0);
account.withdraw(20.0);
cout << "Account balance: INR " << account.getBalance() << endl; // Outputs: Account balance: INR 130.0
return 0;
}
In this example, the BankAccount class encapsulates data (balance) and functions (deposit, withdraw, and getBalance). The balance attribute is private, meaning it cannot be accessed directly from outside the class; instead, it must be accessed through the public functions. This way, we ensure the balance cannot be changed arbitrarily, thus maintaining data integrity.
Member Functions
Member functions (also known as methods) in C++ are functions that belong to objects in a class. They operate on an object and can access the data members and functions of the class to which they belong.
Here's a simple example:
#include <iostream>
using namespace std;
class Rectangle {
private:
double width;
double height;
public:
void setValues(double w, double h) { // Member function
width = w;
height = h;
}
double area() { // Member function
return width * height;
}
};
int main() {
Rectangle rect;
rect.setValues(4.0, 2.0);
cout << "Area: " << rect.area() << endl; // Outputs: Area: 8
return 0;
}
In this example, Rectangle is a class with two private data members: width and height. The class has two member functions:
- setValues(double w, double h): This function sets the values of width and height. It takes two parameters, w and h.
- area(): This function calculates and returns the area of the rectangle. It doesn't take any parameters.
In main(), we create an object rect of type Rectangle. We use the setValues() member function to set the values of width and height, and then we call the area() member function to calculate the area of the rectangle.
Access to member functions is controlled by their access specifiers (public, private, protected). Here, both member functions are public, so they can be accessed from outside the class.
Constructors and its types
In C++, a constructor is a special type of member function that gets called whenever an object of its associated class is created. Constructors have the same name as the class and don't have a return type.
Here are the types of constructors in C++:
-
Default constructor: A constructor that accepts no parameters is called a default constructor. If no constructor is provided in the class, the compiler provides a default one.
class MyClass { public: MyClass() { cout << "Default constructor called" << endl; } };
-
Parameterized constructor: A constructor that accepts parameters is called a parameterized constructor. It's used to provide different initial values for the distinct objects.
class MyClass { private: int value; public: MyClass(int v) : value(v) { cout << "Parameterized constructor called, value: " << value << endl; } };
-
Copy constructor: A copy constructor is a member function that initializes an object using another object of the same class. If no copy constructor is provided, the compiler provides a default one.
In this example, we're using the MyClass(const MyClass& source) copy constructor to create a new MyClass object that is a copy of an existing one.class MyClass { public: int value; // Parameterized constructor MyClass(int val) : value(val) {} // Copy constructor MyClass(const MyClass& source) : value(source.value) { cout << "Copy constructor called, value: " << value << endl; } };
-
Move constructor(C++11 and later): This is used to initialize an object using a temporary object of the same class, which can improve performance.
In this example, MyClass(MyClass&& source) is a move constructor that takes a temporary MyClass object and "steals" its resources.class MyClass { public: unique_ptr
value; // Move constructor MyClass(MyClass&& source) : value(move(source.value)) { cout << "Move constructor called" << endl; } };
Please note that the system automatically provides a default and a copy constructor if you don't provide them, but sometimes it's necessary to define them yourself, particularly when working with dynamic memory or resources like file handles.
Access Specifiers
Access specifiers in C++ are keywords that define how to access the members (functions and variables) of a class. There are three types of access specifiers:
- Public: Members declared under the public specifier are accessible from anywhere in the program where the object of that class is visible.
- Private: Members declared as private can be accessed only within the methods of the same class. Therefore, private members are not visible from outside the class.
- Protected: Protected is very similar to private, but it has one additional feature: it allows a child class to access the protected members inherited from the parent class.
Here's a simple example demonstrating these access specifiers:
#include<iostream>
using namespace std;
class MyClass {
public:
int publicVar;
private:
int privateVar;
protected:
int protectedVar;
};
class ChildClass : public MyClass {
public:
void accessProtected() {
protectedVar = 10; // Allowed, as protected members are accessible in child classes
cout << "Protected Var: " << protectedVar << endl;
}
};
int main() {
MyClass obj;
obj.publicVar = 5; // Allowed
// obj.privateVar = 5; // Not allowed, gives a compilation error
// obj.protectedVar = 5; // Not allowed, gives a compilation error
ChildClass childObj;
childObj.accessProtected(); // Allowed
return 0;
}
In this code:
- publicVar is a public member of MyClass, so it can be accessed directly in main().
- privateVar is a private member, so it cannot be accessed directly outside MyClass.
- protectedVar is a protected member, so it cannot be accessed directly outside MyClass, but it can be accessed within ChildClass because ChildClass inherits from MyClass.
Scope Resolution Operator
The Scope Resolution Operator (::) in C++ is used to define a function outside a class or to access a global variable within a scope where a local variable with the same name exists.
Here's an example of defining a function outside of a class using ::#include <iostream>
using namespace std;
class MyClass {
public:
void myFunction();
};
// Defining myFunction outside the class using Scope Resolution Operator
void MyClass::myFunction() {
cout << "Function called" << endl;
}
int main() {
MyClass obj;
obj.myFunction(); // Outputs: Function called
return 0;
}
In this example, myFunction is declared inside MyClass but defined outside the class using the scope resolution operator ::.
Here's an example of using :: to access a global variable when a local variable with the same name exists:
#include <iostream>
using namespace std;
int var = 10; // Global variable
int main() {
int var = 20; // Local variable
cout << "Local var: " << var << endl; // Outputs: Local var: 20
cout << "Global var: " << ::var << endl; // Outputs: Global var: 10
return 0;
}
In this example, the local variable var shadows the global variable var within the main() function. To access the global var within main(), we use the scope resolution operator ::.
Virtual Function
A virtual function, declared with the keyword 'virtual' in a base class, is designed for redefinition in derived classes. This mechanism supports runtime polymorphism in C++, allowing interchangeability of base and derived class objects, ensuring the correct function call.
Let us learn about it with an example:
#include <iostream>
using namespace std;
class Base {
public:
virtual void show() {
std::cout << "In Base" << endl;
}
};
class Derived : public Base {
public:
void show() override {
std::cout << "In Derived" << endl;
}
};
int main() {
Base* basePtr = new Derived();
basePtr->show(); // Outputs: "In Derived", due to the 'virtual' keyword
delete basePtr;
return 0;
}
Here, new
is used to dynamically allocate memory for a Derived pointer object on the heap, and return a pointer to the start of it. This pointer is stored in basePtr. When basePtr->show()
is called it outputs "In Derived". This is due to the virtual
keyword allowing for late (or dynamic) binding. delete
is used to deallocate (or free up) the memory that was previously allocated with new
. After this line, the memory where the Derived object was stored is freed up and basePtr
should not be used.
Early Binding and Late Binding
Early Binding (or Static Binding) and Late Binding (or Dynamic Binding) refer to when the association of a function call to a function body is made.
Early Binding happens at compile-time. The association of a function to its function call is made before the program runs. On the other hand, Late Binding occurs at runtime, where the compiler determines the type of an object at runtime and then binds the function call to its definition.
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() { //early binding
cout << "Drawing a shape.\n";
}
};
class Circle: public Shape {
public:
void draw() { //late binding (using function overriding)
cout << "Drawing a circle.\n";
}
};
int main() {
Shape shape;
Circle circle;
shape.draw(); // Outputs: Drawing a shape. (Early binding)
circle.draw(); // Outputs: Drawing a circle. (Late binding)
return 0;
}
Here, when you call draw() on shape, it knows at compile time it's going to call Shape::draw(). That's Early Binding. When you call draw() on circle, because draw() is a virtual function and circle is an instance of the derived class Circle, it doesn't decide until runtime which draw() it will call. This is Late Binding. Lets look at another example.
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() { // This function is now virtual!
cout << "Drawing a shape.\n";
}
};
class Circle: public Shape {
public:
void draw() override { // This function overrides the base function!
cout << "Drawing a circle.\n";
}
};
int main() {
Shape* shape = new Shape();
Shape* circle = new Circle(); // circle is a Shape pointer that points to a Circle object
shape->draw(); // Outputs: Drawing a shape. (Late binding, but since it's a Shape object, Shape's draw() is called)
circle->draw(); // Outputs: Drawing a circle. (Late binding)
delete shape;
delete circle;
return 0;
}
Operator Overloading
Operator Overloading is a compile-time polymorphism concept that allows you to redefine the way an operator works for user-defined types such as classes and structures. You can redefine or overload most of the built-in operators available in C++.
#include <iostream>
class Counter {
int count = 0;
public:
void operator++() {
count++;
}
int getCount() const {
return count;
}
};
int main() {
Counter c;
++c;
std::cout << c.getCount() << std::endl;
return 0;
}
This code demonstrates a simple counter using operator overloading in C++. It defines a class called Counter with a private member variable count initialized to 0. The class overloads the pre-increment operator (++) to increment the count by 1.
In the main function, an instance of the Counter class is created, and the overloaded ++ operator is used to increment the count. The current count value is then printed using the getCount member function.
The code showcases how operator overloading can be used to redefine the behavior of operators, providing custom functionality for user-defined types. In this case, it allows the Counter object to be incremented in a natural and intuitive way.
Friend Function
A friend function in C++ is a function that has the right to access all private and protected members (variables and functions) of the class, even though it is not itself a member function of that class. This helps enhance the flexibility of the code where certain functions require direct access to class members.
Here's a simple example of using a friend function to add two numbers:
#include <iostream>
using namespace std;
class Number {
int num;
public:
Number(int n) : num(n) {}
// Declaration of friend function
friend int addFive(Number n);
};
// Definition of friend function
int addFive(Number n) {
return n.num + 5; // can access private member 'num' directly
}
int main() {
Number n(7);
cout << addFive(n); // Outputs: 12
return 0;
}
In this example, addFive() is a friend function to class Number. Even though addFive() isn't a member function of Number, it can directly access the private data member num. This enables addFive() to add 5 to the value of num.
Friend Class
A friend class in C++ is a class whose members have the right to access all private and protected members of another class. It enhances the flexibility of the code where certain classes need direct access to another class's members.
Here's a simple example to demonstrate a friend class:
#include <iostream>
using namespace std;
class ClassB; // Forward declaration
class ClassA {
private:
int numA;
public:
ClassA() : numA(123) {}
friend class ClassB; // ClassB is a friend class of ClassA
};
class ClassB {
public:
void showA(ClassA& a) {
// Since ClassB is a friend of ClassA, it can access private members of ClassA
cout << "ClassA::numA: " << a.numA << endl;
}
};
int main() {
ClassA a;
ClassB b;
b.showA(a); // Outputs: ClassA::numA: 123
return 0;
}
In this code, ClassB is a friend of ClassA. This means that ClassB can access the private and protected members of ClassA. In the showA method of ClassB, it directly accesses the private data member numA of ClassA.
Pointers
A pointer in C++ is a variable that stores the memory address of another variable. Pointers are used for dynamic memory allocation and efficient manipulation of data.
#include <iostream>
using namespace std;
int main() {
int num = 10; // Declare an integer variable
int* ptr = &num ; // Declare a pointer variable and store the address of num
cout << "Value of num: " << num << endl;
cout << "Address of num: " << &num << endl;
cout << "Value of ptr (address of num): " << ptr << endl;
cout << "Value that ptr points to (value of num): " << *ptr << endl;
return 0;
}
In this example:
- num is an integer variable with a value of 10.
- ptr is a pointer variable that holds the memory address of num. We get the address of num using the address-of operator &.
- &num gives the memory address of num.
- ptr gives the memory address stored in the pointer ptr (which is the address of num).
- *ptr gives the value at the memory address stored in ptr (which is the value of num). The * here is used as the dereference operator, not for multiplication.
- Remember, the actual memory addresses on your system will be different, as they are allocated by your system at runtime.
Pointers in classes and objects
Pointers can certainly be used with classes and objects. A pointer to a class is not much different from a pointer to an integer or any other variable. The most significant difference is how to access the members of the object (member variables and methods).
To access members of a class using a pointer, you need to use the arrow operator (->).
Here's a simple example:
#include <iostream>
using namespace std;
class MyClass {
public:
int myVar;
void myFunc() {
cout << "This is inside the function!" << endl;
}
};
int main() {
MyClass obj;
obj.myVar = 10;
MyClass *ptr = &obj; // Declare a pointer to obj
cout << "Value of myVar: " << ptr->myVar << endl; // Access myVar through pointer
ptr->myFunc(); // Call myFunc() through pointer
return 0;
}
In this code:
- We declare an object obj of MyClass.
- We declare a pointer ptr to MyClass, and make it point to obj.
- To access the member variable myVar and the member function myFunc(), we use the arrow operator ->.
Please note that when working with pointers, it's important to ensure that they are always initialized and that you don't go beyond the bounds of allocated memory, as this can lead to undefined behavior or program crashes.