Object-Oriented Programming in C++
Introduction to OOP
Programming with Objects
Imagine building something with LEGOs. Instead of starting from scratch with raw plastic, you have pre-made bricks. Some are standard blocks, some are wheels, and some are clear for windows. You combine these specialized pieces to build a complex model. Object-Oriented Programming, or OOP, works in a similar way. It's a style of programming that organizes code around 'objects' rather than just lists of instructions.
object
noun
A self-contained unit that consists of both data (properties or attributes) and methods (functions or behaviors) that operate on that data.
Objects are created from a blueprint called a class. A class defines the properties and methods that all objects of that type will have. For example, you could have a Dog class. This blueprint would state that all dog objects have properties like name and breed, and methods like bark() and wagTail().
This approach helps organize complex programs into modular, reusable, and easier-to-manage pieces. Let's explore the three core principles that make this possible in C++.
Encapsulation
Encapsulation is the bundling of data and the methods that operate on that data into a single unit: the class. A key part of this is data hiding, which means restricting direct access to an object's internal state.
Think of a car. You interact with it through a simple interface: a steering wheel, pedals, and a gear shift. You don't need to know how the engine, transmission, or electronics work internally. The complex machinery is hidden, or encapsulated, protecting it from misuse and simplifying how you use it. You can't just reach in and manually spin a gear in the transmission; you have to use the gear shift.
Encapsulation turns an object into a 'black box.' You know what it does, but not how it does it. You interact with its public interface, not its private internals.
In C++, we use access specifiers like public and private to control this. Public members can be accessed from outside the class, forming the object's interface. Private members can only be accessed by the class's own methods, protecting the internal data.
#include <iostream>
#include <string>
class Car {
private:
// These members are hidden from the outside world.
std::string model;
int speed;
public:
// The constructor is a public method to create a Car object.
Car(std::string m) {
model = m;
speed = 0; // Car starts stationary
}
// Public method to accelerate (changes private data safely).
void accelerate(int amount) {
speed += amount;
std::cout << model << " is now going " << speed << " km/h." << std::endl;
}
// Public method to get the speed.
int getSpeed() {
return speed;
}
};
int main() {
Car myTesla("Model S");
myTesla.accelerate(50);
// Direct access to speed is blocked by 'private':
// myTesla.speed = -100; // This would cause a compile error!
return 0;
}
Inheritance
Inheritance allows a new class, called a derived or child class, to be based on an existing class, the base or parent class. The child class inherits the properties and methods of the parent, allowing you to reuse code and create a logical hierarchy.
For instance, an ElectricCar is a specific type of Car. It has everything a normal car has (a model, speed) but also includes unique features, like a battery. Instead of building an ElectricCar class from scratch, we can have it inherit from the Car class and simply add the new features.
This promotes the "Don't Repeat Yourself" (DRY) principle. You write the common code once in the base class, and it's automatically available to all derived classes.
// We reuse the Car class from before.
// ElectricCar is a derived class of the base class Car.
class ElectricCar : public Car {
private:
int batteryCharge; // Additional property for ElectricCar
public:
// The constructor calls the base class (Car) constructor.
ElectricCar(std::string m) : Car(m) {
batteryCharge = 100;
}
// Additional method for ElectricCar.
void charge() {
batteryCharge = 100;
std::cout << "Battery is fully charged!" << std::endl;
}
};
int main() {
ElectricCar myLeaf("Nissan Leaf");
myLeaf.accelerate(40); // This method is inherited from Car!
myLeaf.charge();
return 0;
}
Polymorphism
Polymorphism, which means "many forms," allows objects of different classes to be treated as objects of a common base class. A key feature is the ability for a method to behave differently depending on the object that calls it. This is typically achieved using virtual functions.
Imagine you have different types of animals, like a Dog and a Cat. Both can make a sound, but they do it differently. If we have a base Animal class with a makeSound() method, polymorphism allows us to call makeSound() on any animal object, and it will automatically perform the correct action—a dog will bark, and a cat will meow.
#include <iostream>
class Animal {
public:
// A virtual function can be overridden by derived classes.
virtual void makeSound() {
std::cout << "Some generic animal sound" << std::endl;
}
};
class Dog : public Animal {
public:
// Override the base class method.
void makeSound() override {
std::cout << "Woof!" << std::endl;
}
};
class Cat : public Animal {
public:
// Override it differently.
void makeSound() override {
std::cout << "Meow!" << std::endl;
}
};
// This function works with any Animal or its derivatives.
void triggerSound(Animal* animal) {
animal->makeSound();
}
int main() {
Dog myDog;
Cat myCat;
triggerSound(&myDog); // Outputs: Woof!
triggerSound(&myCat); // Outputs: Meow!
return 0;
}
In the example above, the triggerSound function doesn't need to know if it's dealing with a Dog or a Cat. It just calls the makeSound method on whatever Animal object it receives. C++ figures out at runtime which version of the method to execute. This makes your code more flexible and easier to extend.
Ready to check your understanding of these core concepts?
What is the primary purpose of organizing code around 'objects' in Object-Oriented Programming?
The principle of hiding an object's complex internal workings and exposing only the necessary parts is known as...
These three principles—encapsulation, inheritance, and polymorphism—are the foundation of Object-Oriented Programming. By organizing code into logical, reusable, and interactive objects, you can build more robust and scalable applications.