23.3 — Agregacja

W wykład 23.2 -- Kompozycja zauważyliśmy, że kompozycja kompozycji do procesu tworzenia obiektów z prostszych. Mówiliśmy także o jednym typie, który został podany do wyszukiwania. W powiązaniu z obiektem jest odpowiedzialny za istnienie części.

W tej zasadzie istnieją drugiemu podtypowi przesłania obiektu, zwanemu agregacją.

Agregacja

Aby zakwalifikować się jako agregacja, cały obiekt i jego część istnieją w następującym związku:

  • Część (element) jest częścią obiektu (klasa)
  • Część (element) może (w razie zastosowania) należeć do więcej niż jednego obiektu (klasy) na raz
  • Część (element) nie jego istnienie nie organizacji obiektu (klasa)
  • Część (element) nie wie o pozostałości obiektu (klasy)

Podobnie jak kompozycja, agregacja jest częścią relacją-całość, gdzie część jest w całości i jest częścią jednokierunkową. Zastosowanie w przypadku części może należeć do więcej niż jednego obiektu na raz, a cały obiekt nie odpowiada za istnienie i istnienie części. Kiedy wytworzona jest agregacja, nie jest odpowiedzialna za tworzenie części. Kiedy agregacja ulegnie zniszczeniu, nie jest spowodowana zniszczeniem części.

Rozważmy na przykład pomiędzy jednostką a jej zniszczeniem. W tym momencie dla uproszczeń, że każda osoba ma adres. Jednak adres ten może należeć do więcej niż jednej osoby na raz: na przykład zarówno do Ciebie, jak i twój współlokatora lub bliskiej Ci osoby. Jednak adres ten nie jest zarządzany przez tę osobę — adres możliwy do wystąpienia, zanim dana osoba tam dotrze, będzie istniała po jej odejściu. Co więcej, człowiek wie, pod jakimkolwiek adresem mieszka, ale adres nie dotyczy, jacy ludzie tam mieszkają. Jest to związek zagregowany.

Alternatywnie myślenie o samochodzie i silniku. Silnik samochodu jest częścią samochodu. I chociaż silnik powinien być do samochodu, może należeć do innych rzeczy, na przykład do osoby, która jest właścicielem samochodu. Samochód nie ponosi odpowiedzialności za powstanie lub zniszczenie silnika. I choć samochód wie, że ma silnik (musi to być, aby gdziekolwiek się pojawił), silnik nie wie, że jest pojazdem.

Jeśli chodzi o modelowanie obiektów fizycznych, zasięg „zniszczone” może być pewnym ryzykiemwne. mógłby się spierać: „Gdyby meteor spadł z nieba i zmiażdżył samochód, czy wszystkie jego części nie uległy zniszczeniu?” Oczywiście, że tak. Ale do wina meteorytu. Ważne jest, że samochód nie jest odpowiedzialny za zniszczenie ich części (ale może to spowodować siłę zewnętrzną).

Można powiedzieć, że modele agregacji mają relacje „ma-a” (wydział nauczycieli, samochód ma silnik).

Podobnie jak w przypadku wystąpienia szkody, część agregacji może być spowodowana lub multiplikatywne.

Implementacja agregacje

Ponieważ agregacje są źródłami w tych sensacjach, które są częścią-całością, są implementowane niezależnie, a rozszerzenie między nimi jest główną semantyczną. W zasadzie dodajemy nasze części do stosowania, używając normalnych zmiennych składowych (lub wskaźników, których proces alokacji i dezalokacji jest stosowany przez klasę ).

W agregacji dodajemy także części jako zmienne składowe. Te zmienne składowe są zwykle referencjami lub wskaźnikami używanymi do wskazywania obiektów, które stanowią poza zakresem klasycznym. W przypadku agregacji zwykle albo przyjmuje się obiekty, które są używane, jako parametry konstruktorów, albo zaczynają się pusta, a podobiekty są dodawane później za pomocą funkcji dostępu lub operatorów.

Ponieważ te części poza zakresem klasy, gdy klasa zostanie zniszczona, wskaźnik lub zmienna składowa odnosi się do usunięcia (ale nie zostało usunięte). W związku z tym samej części nadal będzie.

Przyjrzyjmy się bardziej szczegółowo przykładowi administratora i wydziału. W tym wykonaniu kilka uproszczeń: Po pierwsze, na wydziale będzie działać tylko jeden nauczyciel. Po drugie, nauczyciel nie będzie wiedział, do poszczególnych działów należy.

#include <iostream>
#include <string>
#include <string_view>

class Teacher
{
private:
  std::string m_name{};

public:
  Teacher(std::string_view name)
      : m_name{ name }
  {
  }

  const std::string& getName() const { return m_name; }
};

class Department
{
private:
  const Teacher& m_teacher; // This dept holds only one teacher for simplicity, but it could hold many teachers

public:
  Department(const Teacher& teacher)
      : m_teacher{ teacher }
  {
  }
};

int main()
{
  // Create a teacher outside the scope of the Department
  Teacher bob{ "Bob" }; // create a teacher

  {
    // Create a department and use the constructor parameter to pass
    // the teacher to it.
    Department department{ bob };

  } // department goes out of scope here and is destroyed

  // bob still exists here, but the department doesn't

  std::cout << bob.getName() << " still exists!\n";

  return 0;
}

W tym przypadku bob jest stworzony produkt od department, a następnie przekazywany do konstruktora department. Kiedy department jest zniszczone, m_teacher od zniszczenia zniszczonego, ale sam nauczyciel nie uległ zniszczeniu, więc nadal istnieje, zostanie nie wysłany później w main().

Wybierz przesłanie dla tego, co modelujesz

Chociaż w wydanym wydaniu może zostać wysłane na całe oprogramowanie, że nie jest znane, które zostało przekazane przez użytkownika, aby być zarejestrowanym w programie. Kiedy ustalasz, jaki związek z uruchomieniem, najprostszy rodzaj urządzenia, który odpowiada potrzebom, a nie tę, który wydaje się najlepszym pasować w rzeczywistym kontekście.

Na przykład, jeśli istnieje symulator warsztatu blacharskiego, możesz zaimplementować samochód i silnik jako sumę, aby silnik mógł zostać usunięty i odłożony na półkę na później. Jeśli tylko to działanie jest skutkiem zaimplementowania samochodu i silnika jako kompozycji, ponieważ w tym przypadku silnik nigdy nie będzie działać poza sytuacją.

Najlepsza praktyka

Zaimplementuj typowy typ relacji, który zapewnia zastosowanie programu, a nie, co jest uznawane za uzasadnione w działaniu.

Podsumowanie i agregacja

Kompozycja:

  • Zazwyczaj używanych normalnych wartości składowych
  • Może wspólne wartości, jeśli klasa sama obsługuje alokację/delokację obiektów
  • Odpowiedzialny za tworzenie/enie części

Agregacje:

  • Za używanie znaku lub uwagi referencyjnej, poza zakresem klasy agregatu lub odwoływania się do nich
  • Nie odpowiada za tworzenie/niszczenie części

Warto podkreślić, że koncepcjae merytoryczne i agregacji można swobodnie zrozumieć w ramach tej samej klasy. Całkiem możliwe jest napisanie klasy odpowiedzialnej za tworzenie/zniszczenie niektórych części, ale nie innych. Na przykład nasza klasa Działu może mieć imię i nauczyciel. Nazwa prawdopodobnie zostałaby dodana do Departamentu w drodze składu oraz została stworzona przez utworzona i zniszczona wraz z Departamentem. Z drugiej strony nauczyciel został dodany do działu poprzez agregację i urządzenie sterujące/zniszczone.

Chociaż agregacje mogą być ultrawysokie, są również dodatkowe dodatkowe, boczne agregacje, które nie radzą sobie z dezalokacją swoich części. Dealokacja tworzenia się strony rozszerzonej. Jeśli strona zewnętrzna nie ma już związku z porzuceniem części, lub jeśli po prostu zapomniano (zakładając, że klasa sobie z tym zadziała), nastąpiło zdarzenie pamięci.

Z tego powodu konsekwencje być preferowane zamiast agregacji.

Kilkażeń/errata

Z powodu wystąpienia głównych komunikatów, w przypadku powiadomienia, agregacji nie jest napisane — powiadomienia o wystąpieniu, że inne materiały referencyjne definiują ją inaczej niż my. W porządku, po prostu bądź tego świadomy.

I ostatnia uwaga: w lekcji 13.7 — Wprowadzenie do struktur, elementów i wybranych elementów zdefiniowaliśmy zagregowane typy danych (takie jak struktury i klasy) jako typy danych, które grupują razem wiele zmiennych. W swoich podróżach po C++ można także natknąć się na termin klasa zagregowana , która jest organizacją lub klasą, która nie ma wspólnych konstruktorów, destruktorów ani przeciążonych przypisań, ma wszystkie elementy zewnętrzne i nie korzystają z zwiedzeń — jest to wspólne struktury danych. pomimo podobieństwa w nazewnictwie, agregaty i agregacja są różne i nie należy ich mylić.

std::reference_wrapper

W Department/Teacher przykład powyżej użyliśmy odniesienia w Department do przechowywania Teacher. Działa to dobrze, jeśli jest tylko jeden Teacher, ale co, jeśli wydział ma wielu nauczycieli? Chcielibyśmy tych nauczycieli na liście (np. std::vector), ale stałe tablice i różne standardowe listy bibliotek nie mogą być odniesione (ponieważ elementy listy muszą być przypisywalne, a odniesione nie można ponownie przypisać).

std::vector<const Teacher&> m_teachers{}; // Illegal

Zamiast odniesionych moglibyśmy być przekazani, ale do przesłanego przez Ciebie przekazania lub przekazania kontroli zerowych. W Department/Teacher nie chcemy zezwalać na wskaźniki zerowe. Aby mieć dziesięć problemów, istnieje std::reference_wrapper.

Zasadniczo std::reference_wrapper to klasa, która działa jak referencja, ale umożliwia także przypisywanie i kopiowanie, więc jest kompatybilny z listami języka jak std::vector.

Dobra jest taka, że ​​​​tak nie akceptuje, jak to działa, aby z niedozwolonym. Wszystko, co musisz wiedzieć, do trzech rzeczy:

  1. std::reference_wrapper znajduje się w nagłówku <funkcjonalny>.
  2. Kiedy tworzywosz std::reference_wrapper obiekt opakowany, obiekt nie może być obiektem anonimowym (ponieważ obiekty anonimowe mają zakres terytorialny, a do pozostawienia naprawionego w zawieszeniu).
  3. jeśli chcesz mieć obiekt z std::reference_wrapper, get() funkcja składowa.

Oto przykład użycia std::reference_wrapper w std::vector:

#include <functional> // std::reference_wrapper
#include <iostream>
#include <vector>
#include <string>

int main()
{
  std::string tom{ "Tom" };
  std::string berta{ "Berta" };

  std::vector<std::reference_wrapper<std::string>> names{ tom, berta }; // these strings are stored by reference, not value

  std::string jim{ "Jim" };

  names.emplace_back(jim);

  for (auto name : names)
  {
    // Use the get() member function to get the referenced string.
    name.get() += " Beam";
  }

  std::cout << jim << '\n'; // prints Jim Beam

  return 0;
}

Aby wyprowadzić zestaw powszechny odniesień, wystąpilibyśmy const przed std::string w dziesięć sposobów

// Vector of const references to std::string
std::vector<std::reference_wrapper<const std::string>> names{ tom, berta };

Czas quizu

Pytanie nr 1

Czy chciałbyś to zrobić bardziej szczegółowe, że dodatkowe elementy jako kompozycja lub połączenie?
a) Kolorowa piłka
b) Pracodawca zatrudniający wiele osób
c) Wydziały na uniwersytecie
d) Twój wiek
e) Worek kulek

Pokaż rozwiązanie

Pytanie nr 2

Z aktualizacja Department/Teacher przykład tak, aby Department mogł obsłużyć wielu nauczycieli. Musi zostać dostarczone kod:

#include <iostream>

// ...

int main()
{
  // Create a teacher outside the scope of the Department
  Teacher t1{ "Bob" };
  Teacher t2{ "Frank" };
  Teacher t3{ "Beth" };

  {
    // Create a department and add some Teachers to it
    Department department{}; // create an empty Department

    department.add(t1);
    department.add(t2);
    department.add(t3);

    std::cout << department;

  } // department goes out of scope here and is destroyed

  std::cout << t1.getName() << " still exists!\n";
  std::cout << t2.getName() << " still exists!\n";
  std::cout << t3.getName() << " still exists!\n";

  return 0;
}

To powinno zostać wydrukowane:

Department: Bob Frank Beth
Bob still exists!
Frank still exists!
Beth still exists!

Pokaż wskazówkę

Pokaż rozwiązanie

guest
Twój adres e-mail nie wychodzi składniki
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze powodujące z poprawkami usuniętymi po przetworzeniu, aby rozwiązać problem. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
306 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze