Page Content

Tutorials

Containership In C++: Understanding “Has-A” Relationships

Containership in C++

In object-oriented programming, containership also commonly called composition or aggregation describes a basic “Has A” relationship between classes. When an object of one class uses or contains an object of another class as one of its members, this relationship is created.

The idea is broken down as follows:

Meaning: Containership, often known as nesting, is the relationship that occurs when a class has objects of another class as its members. The container class is the class that contains these items. Because the objects of the enclosed class are members of the objects of the composite class (the container), this relationship is conceived as a “has-a” relationship.

Encapsulation: A strong and adaptable way to encapsulate data and methods that work with it is through classes. By enabling one class to “encapsulate” the data and functions of another class inside itself, containership takes advantage of this. This implies that the container class can provide a controlled interface to its member objects’ private elements by accessing their public interface.

Purpose and Benefits

Code Reuse: By adding an object from an existing class as a member, you can reuse code rather than inherit it, enabling the containing class to benefit from the contained class’s features.

Logical Grouping: It is possible to keep related information together. For instance, the name of the account holder may be represented by a string object in an Account object.

Loose Coupling: Modifications to the enclosed class’s internal implementation are less likely to impact the containing class when member objects are accessed via their public interfaces, encouraging more modular and maintainable design.

Distinction from Inheritance

The “Has A” relationship must be distinguished from the “Is A” relationship, which is represented by inheritance. A derived class “is a kind of” its source class in inheritance, taking on its traits. With containership, a class does not explicitly inherit the type or behaviour of another class in the same hierarchical sense; instead, it merely “has” an object of that class. For example, although a car “has an” engine, it is not an engine.

Code Example for Containership (“Has A” Relationship)

The way a Car class “has a” Engine object is illustrated in this example.

#include <iostream>
#include <string>
// Define the 'Engine' class
class Engine {
private:
    std::string type;
    int horsepower;
public:
    // Constructor for Engine
    Engine(std::string t, int hp) : type(t), horsepower(hp) {
        std::cout << "Engine '" << type << "' with " << horsepower << " HP created." << std::endl;
    }
    // Destructor for Engine
    ~Engine() {
        std::cout << "Engine '" << type << "' destroyed." << std::endl;
    }
    // Member function to get engine details
    void displayEngineDetails() const {
        std::cout << "  Engine Type: " << type << ", Horsepower: " << horsepower << std::endl;
    }
    // Getters for properties (optional, but good practice)
    std::string getType() const { return type; }
    int getHorsepower() const { return horsepower; }
};
// Define the 'Car' class, which 'has an' Engine
class Car {
private:
    std::string make;
    std::string model;
    Engine carEngine; // This is the Containership/Composition: Car has an Engine object
public:
    // Constructor for Car, which initializes the Engine member
    Car(std::string mk, std::string mdl, std::string engineType, int engineHP)
        : make(mk), model(mdl), carEngine(engineType, engineHP) { // Initialize carEngine using Engine's constructor
        std::cout << "Car '" << make << " " << model << "' created." << std::endl;
    }
    // Destructor for Car
    // The carEngine member's destructor will be automatically called when a Car object is destroyed.
    ~Car() {
        std::cout << "Car '" << make << " " << model << "' destroyed." << std::endl;
    }
    // Member function to display car details, including engine details
    void displayCarDetails() const {
        std::cout << "Car Details: " << make << " " << model << std::endl;
        carEngine.displayEngineDetails(); // Car object uses its contained Engine object
    }
};
int main() {
    std::cout << "--- Demonstrating Containership ---" << std::endl;
    // Create a Car object. This implicitly creates an Engine object as part of the Car.
    Car myCar("Toyota", "Camry", "V6", 280);
    std::cout << std::endl;
    // Use the Car object to display its details, which in turn uses the Engine's details.
    myCar.displayCarDetails();
    std::cout << std::endl;
    // When myCar goes out of scope (at the end of main), its destructor will be called,
    // and then the carEngine member's destructor will be called automatically.
    std::cout << "--- End of program ---" << std::endl;
    return 0;
}

Output

--- Demonstrating Containership ---
Engine 'V6' with 280 HP created.
Car 'Toyota Camry' created.
Car Details: Toyota Camry
  Engine Type: V6, Horsepower: 280
--- End of program ---
Car 'Toyota Camry' destroyed.
Engine 'V6' destroyed.

You can also read RTTI Run-Time Type Information In C++’s Run-Time Insights

Agarapu Geetha
Agarapu Geetha
My name is Agarapu Geetha, a B.Com graduate with a strong passion for technology and innovation. I work as a content writer at Govindhtech, where I dedicate myself to exploring and publishing the latest updates in the world of tech.
Index