C++. Types of relations between classes: is-a, has-a, uses. Examples
Contents
- 1. Types of relationships between classes. Classification
- 2. An example of the simplest type of is-a relationship (inheritance)
- 3. Examples of a has-a relationship between classes
- Related topics
Search other websites:
1. Types of relationships between classes. Classification
Two types of relationships are possible between classes:
- 1. A relationship of type is-a (is a relationship), in which one class is a subtype of another class. In this regard, one class expands (details) the capabilities of another class. The scope of the class is enhanced through the use of inheritance.
- 2. A relation in which there is a relationship between two classes. Two subtypes of the relationship between classes are distinguished here:
- 2.1. A has-a relationship (the class contains another class). In this case, one or more instances of another class are declared in the class. In this respect, two cases of interaction are possible. The first case is when the object (instance) that is declared in the class is not part of the class (aggregation) and its use does not affect the functional work of the class. The second case is when an object that is declared in a class is an integral part of this class (composition).
- 2.2. A relation of type uses (class “uses a different class). In this case, the class contains the program code of another nested class to which it has access. More details about the features of using relations of type uses are described here.
⇑
2. An example of the simplest type of is-a relationship (inheritance)
The essence of an is-a relationship is that a class is a subspecies of another class. In this example, the Circle base class is extended by the CircleColor class. The CircleColor class is a subspecies of the Circle class and adds a color field to it.
The following elements are implemented in the Circle class:
- internal private fields x, y, r;
- constructor Circle() with 3 parameters that fill the values of internal fields;
- access methods GetXYR(), SetXYR() to the class fields;
- function Area() for calculating the area of a circle.
The CircleColor derived class implements fields and methods that complement the Color class:
- internal private field color of class;
- the CircleColor() constructor with 4 parameters, which calls the constructor of the Color class;
- methods GetColor(), SetColor() to access the hidden variable color.
Demonstration text of the program in C++ as follows:
#include <iostream> using namespace std; // C++. Demonstration of the relationship is-a // A class that implements a circle - the base class class Circle { // 1. Internal private fields of class private: double x, y; // Circle center coordinates double r; // radius of the circle public: // 2. Class constructor Circle(double x, double y, double r) { this->x = x; this->y = y; if (r > 0) this->r = r; else this->r = 1.0; } // 3. Access methods void GetXYR(double& x, double& y, double& r) { x = this->x; y = this->y; r = this->r; } void SetXYR(double x, double y, double r) { this->x = x; this->y = y; // correct radius, if necessary if (r > 0) this->r = r; else this->r = 1.0; } // 4. Function Area() - the area of a circle double Area() { const double Pi = 3.141592; return Pi * r * r; } }; // A class that extends the capabilities // of the Circle class - adds color to the circle class CircleColor : public Circle { // 1. Internal variables of the class private: unsigned int color = 0; public: // 2. Constructor CircleColor(double x, double y, double r, unsigned int color) :Circle(x, y, r) { this->color = color; } // 3. Access methods unsigned int GetColor() { return color; } void SetColor(unsigned int color) { this->color = color; } }; void main() { // Demonstration of the relationship is-a between classes // 1. Class Circle // 1.1. Create an instance of class Circle Circle cr(1, 3, 2); // 1.2. Call the method Area() of class Circle double area = cr.Area(); cout << "The instance of class Color:" << endl; cout << "area = " << area << endl; // 1.3. Get the value of instance cr double x, y, r; cr.GetXYR(x, y, r); // 1.4. Display the values on the screen cout << "x = " << x << ", y = " << y << ", radius = " << r << endl; // 2. The instance of class CircleColor // 2.1. Create an instance of CircleColor class CircleColor crCol(2, 4, 6, 1); // 2.2. Get the fields values of crCol instance // 2.2.1. get the value of color from method of CircleColor class unsigned int col = crCol.GetColor(); // 2.2.2. Get the values x, y, r from method of base class Color crCol.GetXYR(x, y, r); // 2.3. Display the values on screen cout << "The instance of class ColorCircle: " << endl; cout << "color = " << col << endl; cout << "x = " << x << endl; cout << "y = " << y << endl; cout << "r = " << r << endl; // 2.4. Invoke the Area() method of base class Circle cout << "area = " << crCol.Area() << endl; }
The result of the program
The instance of class Color: area = 12.5664 x = 1, y = 3, radius = 2 The instance of class ColorCircle: color = 1 x = 2 y = 4 r = 6 area = 113.097
⇑
3. Examples of a has-a relationship between classes
With a has-a relationship, a class contains one or more objects (instances) of another class. There are two kinds of has-a relationships:
- aggregation. This is the case when one or more nested objects are not part of the class. A class can contain any number of such objects (even 0). For a better study of aggregation, see the examples below;
- composition or union. In this case, one or more nested objects is part of the class, that is, without these objects, the logical existence of the class itself is impossible.
⇑
3.1. Aggregation examples for has-a relationship type
In the case of aggregation, a class contains many (one or more) objects of other classes that are not part of this class (do not contain code that complements the work of the class itself).
3.1.1. A shortened example. The Figures class contains instances of the Triangle and Circle classes.
This abbreviated code demonstrates aggregation using the Triangle, Circle, Figures classes as an example. The Figures class can contain a different number of different figures (even 0). Whenever possible, arrays of other figures can be added to the Figures class, for example, a Rectangle. In any case, the Figures class will be fully functional, which means that this is aggregation.
// Class that describes a triangle class Triangle { // Methods and fields of Triangle class // ... }; // A class that implements a circle class Circle { // Methods and fields of Circle class // ... }; // A class that implements various geometric figures. // Used type of relationship - aggregation. class Figures { Triangle tr[10]; // array of triangles unsigned int n_tr; // the number of triangles in the array tr Circle cr[10]; // array of circles unsigned int n_cr; // the number of circles in the array cr // Other fields and class methods // ... };
⇑
3.1.2. Example. The BusStation class contains arrays of instances of the Bus and Car classes
In the example, the BusStation class (Bus Station) contains arrays of instances of the Bus and Car classes. The number of elements in arrays of the Bus and Car classes can vary. Even if there is not a single bus or car at the bus station at a given time, the bus station will function. This is aggregation.
#include <iostream> using namespace std; // C++. The demonstration of has-a relationship // The maximum possible number of vehicles at the bus station const int MAX_VEHICLES = 10; // Clas that describes a car class Car { // 1. Internal private fields of the class private: string model; // car model public: // 2. Class constructors // 2.1. The main constructor of the class with 1 parameter Car(string _model) :model(_model) { } // 2.2. Constructor without parameters, // calls the main constructor Car() : Car("") { } // 3. Methods of access to class fields string GetModel() { return model; } void SetModel(string _model) { model = _model; } // 4. The method of displaying vehicle information void Print() { cout << "model = " << model << endl; } }; // Class Bus class Bus { // 1. Internal hidden fields private: string model; // Brand of the bus unsigned int seats; // number of seats public: // 2. Constructors // 2.1. Constructor with 2 parameters - main constructor Bus(string _model, int _seats) : model(model), seats(_seats) { } // 2.2. Constructor without parameters, // calls the main constructor Bus() : Bus("", 0) {} // 3. Access methods void Get(string& _model, int& _seats) { _model = model; _seats = seats; } void Set(string _model, int _seats) { model = _model; seats = _seats; } // 4. Method that displays information about bus void Print() { cout << "model = " << model << ", seats = " << seats << endl; } }; // Class BusStation - contains arrays of Bus, Car classes class BusStation { private: Bus B[MAX_VEHICLES]; // an array of objects of class Bus unsigned int nBus; // current number of buses Car C[MAX_VEHICLES]; // array of objects of class Car unsigned int nCar; // current number of cars public: // 1. Constructor without parameters BusStation() { nBus = nCar = 0; } // 2. Access methods processing an array of buses B[] // 2.1. Get information about bus by number Bus GetBus(unsigned int number) { // checking if the number is correct if (number < nBus) return B[number]; else { cout << "Error. Incorrect number of a bus." << endl; return Bus("", 0); } } // 2.2. Add a new bus void AddBus(string model, unsigned int seats) { // check if a vehicle can be added if ((nCar + nBus) < MAX_VEHICLES) { nBus++; // increase the number of buses by 1 B[nBus - 1].Set(model, seats); // invoke the method of Bus class cout << "A new bus is added!" << endl; } else { cout << "Cannot add a new bus. Sorry" << endl; return; } } // 2.3. Remove the bus from the bus station by its number void DelBus(unsigned int number) { if (number < nBus) { // cycle of displacement of elements of array B for (int i = number; i < nBus - 1; i++) B[i] = B[i + 1]; nBus--; } } // 3. Access methods processing an array of cars C[] // 3.1. Get a car by its serial number Car GetCar(unsigned int number) { if (number < nCar) return C[number]; else { cout << "Error. Incorrect number of a car." << endl; return Car(""); } } // 3.2. Add a new car to the car park void AddCar(string model) { // Check if there is a parking space if ((nCar + nBus) < MAX_VEHICLES) { nCar++; // increase the number of cars C[nCar - 1].SetModel(model); // add the data of the vehicle cout << "A new car is added!" << endl; } else { cout << "Cannot add a new car. Sorry" << endl; return; } } // 3.3. Remove the car from the parking lot at the specified number void DelCar(unsigned int number) { if (number < nCar) { // Element shift cycle from position number for (int i = number; i < nCar - 1; i++) C[i] = C[i + 1]; nCar--; } } // 4. A method that displays information about // currently located vehicles in a parking lot void Print() { cout << "Info about bus station:" << endl; cout << "nBus = " << nBus << endl; if (nBus > 0) { cout << "Info about buses:" << endl; for (int i = 0; i < nBus; i++) { B[i].Print(); } } cout << "nCar = " << nCar << endl; if (nCar > 0) { cout << "Info about cars:" << endl; for (int i = 0; i < nCar; i++) { C[i].Print(); } } } }; void main() { // Demonstration of using the BusStation class // 1. Declare the instance of class BusStation BusStation bs; // 2. Add some vehicles bs.AddBus("Mercedes Sprinter", 28); bs.AddBus("Renault Traffic", 29); bs.AddCar("Bentley"); bs.AddCar("Renault Sandero"); // 3. Display data about vehicles bs.Print(); }
The result of the program:
A new bus is added! A new bus is added! A new car is added! A new car is added! Info about bus station: nBus = 2 Info about buses: model = Mercedes Sprinter, seats = 28 model = Renault Traffic, seats = 29 nCar = 2 Info about cars: model = Bentley model = Renault Sandero
⇑
3.2. Examples of compositions for the relationship type has-a
3.2.1. A simplified example. The Car class, containing instances of the Vehicle and Wheel classes
The example demonstrates the composition for the classes Vehicle, Wheel, Car. The car (class Car) includes the engine (Vehicle class) and the wheel (Wheel), which are its integral part – it is a composition.
// Class Vehicle class Vehicle { // Fields and methods of the class // ... }; // Class Wheel class Wheel { // Fields and methods of the class // ... }; // Car class - contains required elements, // which are the components of Automobile class Car { // Instances of required classes // that are part of this class are composition private: Vehicle veh; // car contains engine (required) Wheel whl[4]; // car contains engine (required) // Fields and methods of the class // ... };
⇑
3.2.2. A simplified example. The Bike class contains instances of the Wheel and Saddle classes.
Another example of composition. The Bike class contains instances of the Wheel and Saddle classes, which are part of it. A bicycle cannot be without wheels or a saddle, therefore it is a composition.
// Class Wheel class Wheel { // Fields and methods of the class // ... }; // Class Saddle class Saddle { // Fields and methods of the class // ... }; // The Bicycle class, contains the objects of the classes // that are part of the Bicycle (saddle, wheels) - this is a composition class Bike { Saddle sd; // one saddle - part that complements the bike Wheel whl[2]; // two wheels - part of a bicycle // Fields and methods of class Bike // ... };
⇑
Related topics
⇑