Powiedzmy, że mam klasę, Foobar
która używa (zależy od) klasy Widget
. W Widget
dawnych czasach wolud może być zadeklarowany jako pole Foobar
, a może jako inteligentny wskaźnik, jeśli zachodzi potrzeba zachowania polimorficznego, i zostanie zainicjowany w konstruktorze:
class Foobar {
Widget widget;
public:
Foobar() : widget(blah blah blah) {}
// or
std::unique_ptr<Widget> widget;
public:
Foobar() : widget(std::make_unique<Widget>(blah blah blah)) {}
(…)
};
I wszystko byłoby gotowe. Niestety, dzisiaj Java dzieci będą śmiać się z nas, gdy widzą to, i słusznie, za nim par Foobar
i Widget
razem. Rozwiązanie jest z pozoru proste: zastosuj Dependency Injection, aby usunąć konstrukcję zależności z Foobar
klasy. Ale potem C ++ zmusza nas do myślenia o własności zależności. Przychodzą mi na myśl trzy rozwiązania:
Unikalny wskaźnik
class Foobar {
std::unique_ptr<Widget> widget;
public:
Foobar(std::unique_ptr<Widget> &&w) : widget(w) {}
(…)
}
Foobar
twierdzi, że wyłączna własność Widget
tej własności została mu przekazana. Ma to następujące zalety:
- Wpływ na wydajność jest znikomy.
- Jest bezpieczny, ponieważ
Foobar
kontroluje jego żywotnośćWidget
, dzięki czemu zapewnia, żeWidget
nie zniknie nagle. - Jest bezpieczny, że
Widget
nie wycieknie i zostanie odpowiednio zniszczony, gdy nie będzie już potrzebny.
Jest to jednak kosztem:
- Nakłada ograniczenia na sposób
Widget
użycia instancji, np. NieWidgets
można użyć alokacji stosu , nieWidget
można współdzielić.
Współdzielony wskaźnik
class Foobar {
std::shared_ptr<Widget> widget;
public:
Foobar(const std::shared_ptr<Widget> &w) : widget(w) {}
(…)
}
Jest to prawdopodobnie najbliższy odpowiednik języków Java i innych śmieci. Zalety:
- Bardziej uniwersalny, ponieważ umożliwia współdzielenie zależności.
- Utrzymuje bezpieczeństwo (punkty 2 i 3)
unique_ptr
roztworu.
Niedogodności:
- Marnuje zasoby, gdy nie dotyczy udostępniania.
- Nadal wymaga alokacji sterty i nie zezwala na obiekty alokowane na stosie.
Zwykły wskaźnik obserwujący
class Foobar {
Widget *widget;
public:
Foobar(Widget *w) : widget(w) {}
(…)
}
Umieść surowy wskaźnik wewnątrz klasy i przenieś ciężar własności na kogoś innego. Plusy:
- Tak proste, jak to tylko możliwe.
- Uniwersalny, akceptuje tylko każdy
Widget
.
Cons:
- Nie jest już bezpieczny.
- Wprowadza inny podmiot, który jest odpowiedzialny za własność zarówno
Foobar
iWidget
.
Niektóre szalone metaprogramowanie szablonu
Jedyną zaletą, jaką mogę wymyślić, jest możliwość przeczytania wszystkich książek, na które nie znalazłem czasu, gdy moje oprogramowanie się buduje;)
Skłaniam się ku trzeciemu rozwiązaniu, ponieważ jest najbardziej uniwersalne, czymś i tak trzeba zarządzać Foobars
, więc zarządzanie Widgets
jest prostą zmianą. Jednak używanie surowych wskaźników przeszkadza mi, z drugiej strony inteligentne rozwiązanie wskaźnika wydaje mi się złe, ponieważ ograniczają sposób tworzenia zależności.
Czy coś brakuje? A może po prostu wstrzykiwanie zależności w C ++ nie jest trywialne? Czy klasa powinna posiadać swoje zależności, czy tylko je obserwować?
Foobar
to jedyny właściciel? W starym przypadku jest to proste. Ale problem z DI, jak widzę, polega na tym, że oddziela klasę od konstrukcji swoich zależności, a także od własności tych zależności (ponieważ własność jest związana z budową). W środowiskach śmieciowych, takich jak Java, nie stanowi to problemu. W C ++ to jest.
std::unique_ptr
jest to właściwa droga. Możesz użyć,std::move()
aby przenieść własność zasobu z górnego zakresu do klasy.