2016-08-25 22 views
7

Pracuję nad aplikacją dla kontrolera gromadzenia danych. Ma interfejs z wieloma innymi urządzeniami, co z kolei może zapewnić różne typy i ilości danych. W tym celu następujące hierarchia była opracowany (jest to trochę długi, ale pokrywa się ze mną):Bezpieczne przekazywanie odniesień do klasy abstrakcyjnej w C++

  • Podstawową jednostką informacji jest punkt odniesienia. Datum sama jest abstrakcyjną klasą , której potomkowie reprezentują różne typy [fizycznej] ilość: temperatura, ciśnienie, energia, moc, stan przekaźnika, itp. Każda instancja Datum reprezentuje pojedynczy odczyt (w pewnym momencie) .

  • Dane są gromadzone przez Device s, które z kolei zawierają kilka IO s. Instancja Device przedstawia konkretne fizyczne urządzenie gromadzące dane; jego klasa ( potomek abstrakcyjnej klasy Device) reprezentuje model urządzenia, a zawiera cały specyficzny dla modelu kod niezbędny do połączenia się z nim i odczyty z niego odczytów z . Odbywa się to poprzez wywołanie funkcji wirtualnej void Device::update().

  • Każda instancja IO reprezentuje zmienną, którą zbiera urządzenie. Na przykład: , jeśli urządzenie jest wielokanałowym monitorem temperatury, wówczas IO reprezentuje pojedynczy czujnik podłączony do urządzenia. IO można zapytać o wartość przez , wywołując IO::get_value(), która zwraca Datum.

  • Wreszcie klasa Node przechowuje listę wszystkich urządzeń podłączonych do sterownika , innej listy wszystkich OI w tych urządzeniach, i zapewnia metody odpytywania wszystkich urządzeń na raz, poszczególne urządzenia, indywidualne iOS etc.

związki te są (nieco luźno) widoczne w następującym schemacie:

Diagram of class relations

Obecnie dla samego problemu:

w tym wszystkim, wiele wystąpień potomków abstrakcyjnych klas muszą być przekazywane wokół i przechowywane przez cały czas: the węzeł przechowuje Device s i ich IO s, urządzenies sami przechowywania własnych IO s jako dobrze, danych uzyskać utworzone i wrócił i przeszedł wokół i zniszczone, urządzenie i lista IO zostanie zaktualizowane w miejscu, itd. Ale to nie jest jasne, jak wdrożyć wszystkie przechodząc wokół:

  • przechodzące instancje klas abstrakcyjnych według wartości jest oczywiście wykluczone, a nawet to nie były abstrakcyjne, może to doprowadzić do obiektu krojenia.
  • przekazywanie ich przez odniesienie działa dla argumentów funkcji, ale co z tworzeniem i zwracaniem instancji Datum? są tworzone jako zmienne lokalne w metodzie IO :: get_value, a więc są niszczone po powrocie. Ponadto nie jest możliwe przechowywanie referencji, np. w std :: map.
  • wreszcie, przekazywanie wskaźników jest niebezpieczne. Aby zwrócić coś jako wskaźnik należy przydzielić pamięć w metodzie, a następnie wywołać funkcję dzwoniącego, aby zwolnić pamięć po tym, jak zwrócona wartość nie jest już używana. Poza tym, że jest to niewygodne, stwarza niebezpieczeństwo utraty przeciążenia pamięci, wykonanie podwójnej luki lub innej części programu, która usuwa teraz pusty wskaźnik, który wcześniej był tam przechowywany.

Więc jestem w rozterce, w jaki sposób można by realizować solidnego systemu wymiany obiektów, takich jak ten, z bardziej lub mniej niezawodnych sposobów zapewniających obiektów zachowywać jako zmienna przekazywane przez wartość (Pozostań tak długo, jak długo są potrzebne, ale nie dłużej), zachowując typowanie kaczkowe zapewniane przez dziedziczenie wspólnego interfejsu.

+3

Dwa słowa: [smart pointers] (https://pl.wikipedia.org/wiki/Smart_pointer) – NathanOliver

+0

Nie podoba się, że problemem jest "przemijanie". – juanchopanza

+0

Myślę, że tylko konsument Datum będzie zainteresowany typem danych/urządzenia. W przeciwnym razie wszystkie elementy pośrednie będą po prostu przechodzić obok obiektów (które mogą i najprawdopodobniej będą klasami abstrakcyjnymi). Dlatego też, ponieważ punkt odniesienia powinien mieć wystarczającą ilość informacji, których konsument będzie oczekiwał. – sameerkn

Odpowiedz

1

Użyj std::unique_ptr dla unikalnych właścicieli i std::shared_ptr dla współwłasności. Dzięki temu wskaźniki przechodzenia są o wiele bezpieczniejsze.

std :: unique_ptr nie może zostać skopiowany, a wskazany obiekt jest automatycznie zwalniany, gdy właściwość unique_ptr zostanie zniszczona (np. Wykracza poza zakres). I odwrotnie, std :: shared_ptr MOŻNA zostać skopiowany, a obiekt wskazany jest zwalniany, gdy wszystkie kopie shared_ptr zostaną zniszczone.

Jeśli przyrząd kodować z powyższych narzędzi, a także korzystać z std::make_unique (C++ 14) i (C++ std::make_shared 11), w znacznym stopniu wolny od ręcznego new i delete i uniknąć wielu problemów związanych z pamięcią.

Powiązane problemy