top of page

Object-Oriented Programming (OOP) in C++


Object-Oriented Programming (OOP) in C++: Principles and Concepts


Object-Oriented Programming (OOP) is a programming paradigm based on the concept of "objects," which can hold both data and functions. In C++, OOP helps organize and structure code to make it more maintainable, reusable, and easier to understand. The key principles of OOP in C++ include inheritance, abstraction, polymorphism, and encapsulation. Additionally, concepts like composition, aggregation, and association are crucial for modeling relationships between objects.


Key OOP Principles


1. Inheritance

Inheritance allows one class to inherit the properties and behaviors (methods) of another class. It provides a way to create a new class based on an existing class, promoting code reuse and reducing duplication.

Definition: Inheritance enables a new class (child or subclass) to derive properties and behaviors from an existing class (parent or base class). In C++, this is done using the : public keyword.

Example:

#include <iostream>

class Animal {
public:
    void speak() {
        std::cout << "Animal makes a sound." << std::endl;
    }
};

class Dog : public Animal {
public:
    void speak() {
        std::cout << "Woof!" << std::endl;
    }
};

int main() {
    Dog d;

	//output: Woof!
    d.speak();
    return 0;
}

Here, Dog inherits the speak method from Animal, but overrides it to provide a custom implementation.


2. Abstraction

Abstraction is the concept of hiding the complex implementation details and exposing only the necessary features of an object. It simplifies the interaction with complex systems and makes the code more user-friendly.

Definition: In C++, abstraction is typically achieved through abstract classes and pure virtual functions. An abstract class cannot be instantiated and is used to define a common interface for its subclasses.

Example:

#include <iostream>

//abstract class
class Shape {
public:
	//pure virtual function never instantiated
    virtual void draw() = 0;
};

class Circle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing a circle." << std::endl;
    }
};

class Square : public Shape {
public:
    void draw() override {
        std::cout << "Drawing a square." << std::endl;
    }
};

int main() {
    Shape* shape1 = new Circle();
    Shape* shape2 = new Square();
    
	//output: Drawing a circle
    shape1->draw();
	//output: Drawing a square
    shape2->draw();
    
    delete shape1;
    delete shape2;
    return 0;
}

In this example, the Shape class is abstract and defines a pure virtual function draw(). The classes Circle and Square implement the draw() method, providing specific behaviors for different shapes.


3. Polymorphism

Polymorphism allows different classes to be treated as instances of the same class through a common interface. It enables a single function to behave differently depending on the object it is acting upon.

Definition: Polymorphism is achieved in C++. It enables the same function or operator to work on different types of data.

Example :

#include <iostream>
#include <memory>

class Animal {
public:
    virtual void sound() {
        std::cout << "Animal makes a sound." << std::endl;
    }
};

class Dog : public Animal {
public:
    void sound() override {
        std::cout << "Woof!" <<  std::endl;
    }
};

class Cat : public Animal {
public:
    void sound() override {
         std::cout << "Meow!" <<  std::endl;
    }
};

int main() {
    std::unique_ptr<Animal> animal1 = std::make_unique<Dog>();
    std::unique_ptr<Animal> animal2 = std::make_unique<Cat>();

	//output: Woof!
    animal1->sound();
	//output: Meow!
    animal2->sound();

    return 0;
}

Here, Dog and Cat both override the sound() method, allowing polymorphism to work. The Animal* pointer can point to any object of a class derived from Animal, and the correct sound() method is called at runtime.

4. Encapsulation

Encapsulation refers to variables (attributes) and methods (functions) that operate on the data within a single unit or object. It also involves restricting direct access to some of an object's components, which is done using access specifiers like private, protected, and public.

Definition: Encapsulation in C++ is achieved by declaring class members as private or protected, and providing public methods (getters and setters) to interact with these private members.

Example:

#include <iostream>
using namespace std;

class BankAccount {
private:
	//private attribute
    double balance;

public:
    BankAccount(double initialBalance) {
        balance = initialBalance;
    }

    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    double getBalance() {
        return balance;
    }
};

int main() {
    BankAccount account(1000);
    account.deposit(500);

	//output: Balance: 1500
    std::cout << "Balance: " << account.getBalance() << std::endl;
    return 0;
}

In this example, the balance attribute is private, and the deposit and getBalance methods provide controlled access to it. This is an example of encapsulation in action.

Relationships Between Objects: Composition, Aggregation, and Association

1. Composition

Composition is a strong "has-a" relationship between objects. It indicates that one object contains another, and the life cycle of the contained object is dependent on the container object. If the container object is destroyed, the contained object is also destroyed.

Definition: In C++, composition is typically implemented by having a class contain objects of other classes as its members.

Example:

#include <iostream>

class Engine {
public:
    void start() {
        std::cout << "Engine started." << std::endl;
    }
};

class Car {
private:
    Engine engine;  // Car "has-a" Engine

public:
    void start() {
        engine.start();
        std::cout << "Car started." << std::endl;
    }
};

int main() {
    Car car;
	//output: Engine started. Car started.
    car.start();
    return 0;
}

In this example, Car contains an Engine, and if Car is destroyed, the Engine will be destroyed as well.

2. Aggregation

Aggregation is a weaker "has-a" relationship. In aggregation, an object can exist independently of the container object, meaning the contained object does not depend on the container object for its existence.

Definition: In C++, aggregation can be modeled by having a class hold a pointer to another class.

Example:

#include <iostream>

class Student {
public:
    void study() {
        std::cout << "Studying." << std::endl;
    }
};

class Classroom {
private:
	//classroom "has-a" Student,but Student can exist independently
    Student* student;

public:
    Classroom(Student* s) : student(s) {}

    void showStudent() {
        student->study();
    }
};

int main() {
    Student s;
    Classroom classroom(&s);
	//output: Studying.
    classroom.showStudent();
    return 0;
}

Here, the Classroom has an aggregation relationship with Student. A Student can exist independently of the Classroom.

3. Association

Association represents a relationship between two objects where one object may use or reference another, but there is no ownership or lifecycle dependency between them.

Definition: In C++, association can be implemented by having objects reference each other, usually via pointers or references.

Example:

#include <iostream>

class Client {
public:
    void request() {
        std::cout << "Client request." << std::endl;
    }
};

class Order {
private:
	//order "has-a" Client
    Client* client;

public:
    Order(Client* c) : client(c) {}

    void processOrder() {
        client->request();
        std::cout << "Processing order." << std::endl;
    }
};

int main() {
    Client c;
    Order o(&c);
	//output: Client request. Processing order.
    o.processOrder();
    return 0;
}

In this example, Order is associated with Client, but there is no ownership or strong dependency between them.

Conclusion

Object-Oriented Programming in C++ provides powerful mechanisms for organizing code around objects and their interactions. The core principles inheritance, abstraction, polymorphism, and encapsulation help structure software in a modular and maintainable way. Additionally, understanding the relationships between objects, such as composition, aggregation, and association, is essential for creating flexible and scalable systems. These concepts, when applied correctly, can greatly improve the design and development of C++ applications.

 
 
 

Posts récents

Voir tout
Perfect Forwarding

Object& can modify the object but not const Object& (T&&) ---> have access to the object can read but not modify rvalue ---> Object &&...

 
 
 

Commentaires


bottom of page