Powiedzmy, że mam klasę, Foobarktóra używa (zależy od) klasy Widget. W Widgetdawnych 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 Foobari Widgetrazem. Rozwiązanie jest z pozoru proste: zastosuj Dependency Injection, aby usunąć konstrukcję zależności z Foobarklasy. 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) {}
(…)
}
Foobartwierdzi, że wyłączna własność Widgettej własności została mu przekazana. Ma to następujące zalety:
- Wpływ na wydajność jest znikomy.
- Jest bezpieczny, ponieważ
Foobarkontroluje jego żywotnośćWidget, dzięki czemu zapewnia, żeWidgetnie zniknie nagle. - Jest bezpieczny, że
Widgetnie wycieknie i zostanie odpowiednio zniszczony, gdy nie będzie już potrzebny.
Jest to jednak kosztem:
- Nakłada ograniczenia na sposób
Widgetużycia instancji, np. NieWidgetsmożna użyć alokacji stosu , nieWidgetmoż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_ptrroztworu.
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
FoobariWidget.
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 Widgetsjest 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ć?
Foobarto 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_ptrjest to właściwa droga. Możesz użyć,std::move()aby przenieść własność zasobu z górnego zakresu do klasy.