Скажіть, у мене є клас, Foobarякий використовує (залежить від) клас Widget. У добрі дні Widgetwolud буде оголошений як поле в Foobar, або, можливо, як розумний вказівник, якщо потрібна поліморфна поведінка, і вона буде ініціалізована в конструкторі:
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)) {}
(…)
};
І ми б все налаштували і зробили. На жаль, сьогодні діти Java посміятимуться з нас, як тільки вони це побачать, і це справедливо, як це пара Foobarі Widgetразом. Рішення здається простим: застосуйте Dependency Injection, щоб вивести побудову залежностей з Foobarкласу. Але тоді C ++ змушує задуматися про власність залежностей. На думку приходять три рішення:
Унікальний покажчик
class Foobar {
std::unique_ptr<Widget> widget;
public:
Foobar(std::unique_ptr<Widget> &&w) : widget(w) {}
(…)
}
Foobarвимагає одноосібної власності на Widgetце, передане йому. Це має такі переваги:
- Вплив на продуктивність незначний.
- Це безпечно, оскільки
Foobarконтролює термін його експлуатаціїWidget, тому переконайтеся, щоWidgetвін раптово не зникне. - Це безпечно, що
Widgetне просочиться і буде зруйновано належним чином, коли більше не потрібно.
Однак це коштує:
- Він встановлює обмеження щодо використання
Widgetекземплярів, наприклад, неWidgetsможна використовувати виділений стек , неWidgetможна ділитися.
Спільний покажчик
class Foobar {
std::shared_ptr<Widget> widget;
public:
Foobar(const std::shared_ptr<Widget> &w) : widget(w) {}
(…)
}
Це, мабуть, найближчий еквівалент Java та інших зібраних сміттям мов. Переваги:
- Більш універсальний, оскільки він дозволяє обмінюватися залежностями.
- Підтримує безпеку (точки 2 і 3)
unique_ptrрозчину.
Недоліки:
- Витрачає ресурси, коли не використовується спільний доступ.
- Все ще вимагає розподілу купи та забороняє виділяти стеки об'єкти.
Звичайний оглядовий вказівник
class Foobar {
Widget *widget;
public:
Foobar(Widget *w) : widget(w) {}
(…)
}
Помістіть сирий покажчик всередину класу і перекладіть тягар власності на когось іншого. Плюси:
- Настільки просто, як це може вийти.
- Універсальний, приймає будь-яку
Widget.
Мінуси:
- Більше не безпечно.
- Представляє інше підприємство, яке відповідає за право власності на
FoobarіWidget.
Деякі шалені метапрограмування шаблону
Єдина перевага, яку я можу придумати, - це те, що я зможу прочитати всі ті книги, на які я не знаходив часу, поки моє програмне забезпечення буває;)
Я схиляюся до третього рішення, оскільки воно є найбільш універсальним, все-таки треба керувати Foobars, тому управління Widgets- це проста зміна. Однак використання сирих покажчиків мене турбує, з іншого боку, розумне рішення вказівника почуває себе неправильно, оскільки вони змушують споживача залежності обмежувати, як ця залежність створюється.
Я щось пропускаю? Або просто ін'єкція залежності в C ++ не є тривіальною? Чи повинен клас володіти своїми залежностями або просто спостерігати за ними?
Foobarце єдиний власник? У старому випадку це просто. Але проблема з DI, як я бачу, полягає в тому, що він відриває клас від побудови його залежностей, він також відокремлює його від власності на ці залежності (оскільки право власності пов'язане з будівництвом). У сміттєзбірних середовищах, таких як Java, це не проблема. У C ++ це так.
std::unique_ptrце шлях. Ви можете використовуватиstd::move()для передачі права власності на ресурс з верхнього діапазону до класу.