Objects and Encapsulation are two fundamental concepts in Object-Oriented Programming (OOP) that work together to make code more modular, reusable, and maintainable.
In OOP, an object is an instance of a class. A class acts as a blueprint or template for creating objects. An object holds both data (attributes) and functions (methods) that operate on that data.
To create an object in C++, you need to:
new keyword (for dynamic allocation) or direct instantiation (on the stack) to create the object.Example:
#include <iostream>
using namespace std;
class Car {
public:
string model;
int year;
// Method to display car details
void displayInfo() {
cout << "Car Model: " << model << ", Year: " << year << endl;
}
};
int main() {
// Creating an object of the class 'Car'
Car myCar;
myCar.model = "Toyota Corolla";
myCar.year = 2020;
myCar.displayInfo(); // Output: Car Model: Toyota Corolla, Year: 2020
return 0;
}
Explanation:
Car myCar; creates an object myCar of class Car.displayInfo() method is called on myCar to print its state (model and year).Encapsulation is one of the core principles of OOP. It refers to the concept of bundling data (attributes) and the methods (functions) that operate on that data within a single unit or class.
Encapsulation helps in:
The two key mechanisms used to achieve encapsulation in C++ are:
private: Members are hidden and cannot be accessed directly from outside the class.public: Members are accessible from anywhere in the program.protected: Members are accessible within the class and its derived classes.#include <iostream>
using namespace std;
class BankAccount {
private:
double balance; // Private data member
public:
// Constructor to initialize balance
BankAccount(double initialBalance) {
if (initialBalance > 0) {
balance = initialBalance;
} else {
balance = 0;
cout << "Invalid initial balance. Setting balance to 0." << endl;
}
}
// Getter method (accessor) to get balance
double getBalance() {
return balance;
}
// Setter method (mutator) to deposit money
void deposit(double amount) {
if (amount > 0) {
balance += amount;
} else {
cout << "Deposit amount must be positive." << endl;
}
}
// Method to withdraw money
void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
} else {
cout << "Invalid withdrawal amount." << endl;
}
}
// Method to display balance
void displayBalance() {
cout << "Current Balance: $" << balance << endl;
}
};
int main() {
// Create an object of BankAccount with an initial balance
BankAccount account(1000);
// Display the current balance
account.displayBalance();
// Deposit money
account.deposit(500);
account.displayBalance();
// Withdraw money
account.withdraw(200);
account.displayBalance();
// Try invalid operations
account.deposit(-50); // Invalid deposit
account.withdraw(2000); // Invalid withdrawal
return 0;
}
Private Data: The balance attribute is declared as private. This means it cannot be accessed directly from outside the class. You cannot modify or access the balance directly from the main() function.
Public Methods:
deposit() and withdraw() methods are mutators (setters) because they modify the balance.getBalance() method is a getter (accessor) because it allows you to retrieve the balance.displayBalance() method displays the current balance.Control:
deposit() and withdraw() methods have checks to ensure valid operations (e.g., not depositing a negative amount or withdrawing more than the balance). This ensures the integrity of the balance attribute, which is hidden from direct modification.By using encapsulation, we achieve data hiding and controlled access to the internal state of the object, which makes the object more robust and less prone to errors or invalid states.
Control over Data: Encapsulation allows the class to control how its data is accessed and modified. This prevents unauthorized or invalid access and modification of the object's state.
Improved Security: By making data members private and providing public methods to interact with them, you prevent direct access to sensitive data, reducing the risk of unintended modifications or corruption.
Flexibility and Maintenance: If the internal implementation needs to change, you can modify the methods without affecting the external code that interacts with the object. This makes your code more maintainable and flexible.
Data Integrity: Encapsulation ensures that the object's state is always valid by controlling how data is modified (e.g., using validation logic in setters).
Consider a Car class where you can control the speed of the car through methods rather than directly modifying the speed attribute:
class Car {
private:
int speed; // Private data member for speed
public:
// Constructor to initialize the car's speed
Car() {
speed = 0;
}
// Setter method to accelerate the car
void accelerate(int amount) {
if (amount > 0) {
speed += amount;
} else {
cout << "Acceleration amount must be positive." << endl;
}
}
// Getter method to retrieve current speed
int getSpeed() {
return speed;
}
};
int main() {
Car myCar;
myCar.accelerate(50); // Accelerate the car by 50
cout << "Current Speed: " << myCar.getSpeed() << " km/h" << endl;
myCar.accelerate(-10); // Invalid acceleration
cout << "Current Speed: " << myCar.getSpeed() << " km/h" << endl;
return 0;
}
In this example:
speed attribute is encapsulated and cannot be accessed directly.accelerate() method allows controlled modification of the speed.getSpeed() method provides read access to the speed.This kind of encapsulation ensures that the internal state of an object (like speed) is always consistent and that no external code can modify it directly without going through the appropriate checks.
Open this section to load past papers