In object-oriented programming, relationships between objects can be categorized into three main types: composition, aggregation, and association. Each type of relationship has specific properties and use cases.
std::reference_wrapper
is a class that acts like a reference but also allows assignment and copying, making it compatible with containers like std::vector
.
#include <functional> // for std::reference_wrapper
#include <vector>
#include <string>
#include <iostream>
int main()
{
std::string tom = "Tom";
std::string berta = "Berta";
std::vector<std::reference_wrapper<std::string>> names{ tom, berta };
// Accessing the referenced objects
std::cout << names[0].get() << '\n'; // prints "Tom"
std::cout << names[1].get() << '\n'; // prints "Berta"
return 0;
}
Value containers are compositions that store copies of the objects they are holding (and thus are responsible for creating and destroying those copies).
Reference containers are aggregations that store pointers or references to other objects (and thus are not responsible for creation or destruction of those objects).
#include <vector>
#include <string>
class Library
{
private:
std::vector<std::string> books; // value container
public:
Library(const std::initializer_list<std::string>& bookList)
: books(bookList) {}
void addBook(const std::string& book)
{
books.push_back(book);
}
const std::vector<std::string>& getBooks() const
{
return books;
}
};
#include <vector>
#include <functional> // for std::reference_wrapper
#include <string>
class Library
{
private:
std::vector<std::reference_wrapper<std::string>> books; // reference container
public:
Library(const std::initializer_list<std::reference_wrapper<std::string>>& bookList)
: books(bookList) {}
void addBook(std::string& book)
{
books.push_back(book);
}
const std::vector<std::reference_wrapper<std::string>>& getBooks() const
{
return books;
}
};
std::initializer_list
allows classes to be initialized using list initialization.
#include <algorithm> // for std::copy
#include <initializer_list> // for std::initializer_list
#include <iostream>
class IntArray
{
private:
int* m_data;
int m_length;
public:
IntArray(int length)
: m_length(length)
{
m_data = new int[length];
}
IntArray(std::initializer_list<int> list)
: IntArray(static_cast<int>(list.size())) // use delegating constructor to set up initial array
{
std::copy(list.begin(), list.end(), m_data); // initialize our array from the list
}
~IntArray()
{
delete[] m_data;
}
void print() const
{
for (int i = 0; i < m_length; ++i)
std::cout << m_data[i] << ' ';
std::cout << '\n';
}
};
int main()
{
IntArray array{ 1, 2, 3, 4, 5 };
array.print(); // prints: 1 2 3 4 5
return 0;
}
Understanding the relationships between objects is crucial for designing robust and maintainable object-oriented systems. Composition, aggregation, and association each have specific properties and use cases that dictate how objects interact and manage their lifetimes. Using containers like std::vector
and tools like std::reference_wrapper
and std::initializer_list
can help manage these relationships effectively.