A UML class diagram visualizes the different classes of a system, their attributes, operations, and relationships to one another. When applied correctly, UML illustrates exactly how class diagrams can be translated into code. The core problem - and often the most difficult part - is interpreting the diverse class relationships correctly.
In the following, I explain the most important relationship types in UML class diagrams. Each relationship is briefly explained, an everyday example illustrates it, and a section with a C++ code example concludes the section.
The goal of this article is to clarify, even for code-oriented people - like myself - how a UML class diagram is translated into code. The article is aimed at developers with existing UML experience, but can also help beginners taking their first steps with UML.
Association
An association is a “knows-a” relationship; neither object is part or a member of the other. It is the weakest relationship that indicates ownership.
The following image illustrates the directed association “Foo knows a Bar.”

If the arrow is omitted, it means that both classes know each other. For simplicity, I focus on the example of the directed association.
Everyday example: Doctor and Patient - A class “Doctor” and a class “Patient” can have a directed association. A doctor cares for his patients and knows their medical history, but a patient normally does not know all the details about his doctor. In this case, the association is directed from the doctor to the patient.
Example Implementation
class Bar {
…
}
class Foo {
Foo(Bar *bar) : bar_ptr(bar) {}
void SetBar(Bar *bar) { bar_ptr = bar; }
void Func() { bar_ptr->FuncA(); }
…
Bar *bar_ptr; // pointer
};
Aggregation
Aggregation is a special form of association and represents a “has-a” relationship. It means that I have an object that I have borrowed. I can continue to live even if this object no longer exists.
The following image depicts the aggregation “Foo has a Bar.”

An aggregation can occur when a class is a collection or container for other classes, but the contained classes are not strongly dependent on the container. That is, if the container is destroyed, its contents remain unaffected.
One could confuse aggregation with association, since the difference is merely logical: it depends on whether the object is part of the other or not.
Everyday example: Soccer team and players - A soccer team consists of many players. In this case, the soccer team is the whole and the players are the parts. A soccer team “has” players.
Example Implementation
As described above, the implementations of association and aggregation do not differ.
class Bar {
…
}
class Foo {
public:
Foo(Bar *bar) : bar_ptr(bar) {}
void SetBar(Bar *bar) { bar_ptr = bar; }
void Func() { bar_ptr->FuncA(); }
…
Bar *bar_ptr; // pointer
};
Composition
A composition is a special form of aggregation and represents an “owns-a” or “belongs-to” relationship. I own an object and am responsible for its lifetime. If I no longer existed, then the object would also no longer exist.
The following image depicts the composition “Foo owns a Bar.”

This is a stronger form of the “has-a” relationship, implying that the lifetime of the part object (the “possession”) is bound to that of the whole.
Everyday example: Human and Heart - A human owns a heart. A human cannot exist without his heart. If the heart ceases to exist, the human also dies. Here, the composition is from the class “Heart” to the class “Human.”
Example Implementation
// Example 1
class Foo {
public:
Foo() : bar_ptr(nullptr) { bar_ptr = new Bar()}
~Foo() { delete bar_ptr; }
…
private:
…
Bar *bar_ptr; // pointer
};
// Example 2
class Foo {
…
Bar bar;
}
Generalization
Generalization is another term for inheritance. It represents “is-a” relationships. With this, we assign a generic term (category) to an object.
The following image depicts the generalization “Foo is a Bar.”

Everyday example: Animal and Dog - A dog is an animal. In this case, “Animal” would be the base class and “Dog” would be a derived class.
Example Implementation
class Bar {
public:
~virtual Bar() {};
virtual void FuncA() { … /* Bar::FuncA implementation */ }
virtual void FuncB() { … /* Bar::FuncB implementation */ }
…
};
// Generalization of Bar
class Foo : public Bar {
public:
virtual void FuncA() { Bar::FuncA(); … /* Foo::FuncA implementation */}
virtual void FuncB() { Bar::FuncB(); … /* Foo::FuncB implementation */}
}
Dependency
Dependencies represent a “uses-a” relationship between two classes. Here, a change in one class may require changes in the dependent class.
The following image depicts the dependency “Foo uses a Bar.”

Everyday example: Cook and Recipe - A cook depends on a recipe to prepare a dish. If the recipe is changed (e.g., the ingredients or the preparation instructions), the cook must change his method of preparing the dish accordingly.
Example Implementation
class Foo {
...
void F1(Bar y) {…; y.FuncA(); }
void F2(Bar *y) {…; y->FuncB(); }
void F3(Bar &y) {…; y.FuncC(); }
void F4() { Bar y; y.FuncD(); …}
void F5() {…; Y::StaticFunc(); }
...
};
Realization
A realization is a relationship between two classes in which one class implements or “realizes” the behavior defined by another class or, often, by an interface. One could say that it is a “fulfills-the” relationship.
The following image depicts the realization “Foo fulfills the IBar interface.”

Everyday example: It is difficult to find an everyday example, as this concept is hardly tangible in the real world. Roughly speaking, a class could implement an interface “Walkable” that defines a method “walk.” This class could be a “Dog,” a “Human,” or a “Robot” - anything that can “walk.”
Example Implementation
// Abstract interface
class IBar {
~virtual IBar() {};
…
virtual void FuncA() = 0;
virtual void FuncB() = 0;
};
// Realization of interface IBar
class Foo : public IBar{
…
virtual void FuncA() { … /* FuncA implementation */}
virtual void FuncB() { … /* FuncB implementation */}
}
Summary
This article illuminates the relationship types in UML class diagrams and their translation into C++ code. The most important relationship types are association, aggregation, composition, generalization, dependency, and realization. By comparing them with everyday examples and concrete C++ implementations, these concepts are presented in a clear and understandable manner.