#c #dependency-injection #c 11 #unique-ptr
#c #внедрение зависимостей #c 11 #уникальный-ptr
Вопрос:
Я выполнял внедрение зависимостей с использованием необработанных указателей и решил преобразовать свой код для использования shared_ptr. Это работает, но мне интересно, могу ли я использовать unique_ptr вместо этого? В моем примере ниже MyClass будет управлять жизненным циклом службы кредитных карт.
class PaymentProcessor
{
PaymentProcessor(?? creditCardService):
:creditCardService_(creditCardService)
{
}
private:
CreditCardService *creditCardService_;
}
class MyClass
{
public:
void DoIt()
{
creditCardService_.reset(new VisaCardService());
PaymentProcessor pp(creditCardService_);
pp.ProcessPayment();
}
private:
std::unique_ptr<CreditCardService> creditCardService_;
}
Можете ли вы передать unique_ptr другому классу, где другой класс просто «использует» указатель (не владея им ??)? Если да, то это хорошая идея и какой тип параметра должен быть в конструкторе для PaymentProcessor?
Обновить
В примере, показанном выше, я могу в качестве альтернативы создать VisaCardService
переменную в стеке и заставить PaymentProcessor
конструктор использовать ее в качестве ссылочного параметра. Похоже, это рекомендуемая практика C . Однако в случае, когда конкретный тип creditCardService_ неизвестен до времени выполнения (например, пользователь выбирает конкретную службу кредитных карт для использования во время выполнения), является ли использование std::unique_ptr
with references лучшим решением?
Комментарии:
1. Вызовите
get()
функцию-член уникального указателя.2. Согласно вашему обновлению: Да, это был бы вариант использования для
std::unique_ptr
. Но в этом случае этот указатель все равно не обязательно должен быть переменной-членом класса, если он нужен только внутриDoIt
. Вы можете просто сделать его локальнымstd::unique_ptr
с динамически выделяемым хранилищем. Таким образом, объект уничтожается, как только вы покидаете функцию, из-заstd::unique_ptr
интеллектуальных средств уничтожения.3. Я согласен с Кристианом. Если вы не хотите каким-то образом кэшировать службу во многих вызовах, ваш unique_ptr должен иметь минимально возможную область действия — здесь область действия DoIt .
4. @BartoszMilewski: согласен. В моем коде на самом деле мне нужна моя служба для нескольких вызовов. Здесь я пытался сохранить пример небольшим.
Ответ №1:
Можете ли вы передать unique_ptr другому классу, где другой класс просто «использует» указатель (не владея им ??)?
В этом случае измените указатель на ссылку :
class PaymentProcessor
{
public:
PaymentProcessor(CreditCardService amp; creditCardService_):
:creditCardService_(creditCardService_)
{
}
private:
CreditCardService amp;creditCardService_;
};
void DoIt()
{
creditCardService_.reset(new VisaCardService());
PaymentProcessor pp(*creditCardService_);
pp.ProcessPayment();
}
Если вы все еще хотите использовать указатель, вам нужно использовать get
метод :
class PaymentProcessor
{
public:
PaymentProcessor(CreditCardService * creditCardService_):
:creditCardService_(creditCardService_)
{
}
private:
CreditCardService *creditCardService_;
};
void DoIt()
{
creditCardService_.reset(new VisaCardService());
PaymentProcessor pp(creditCardService_.get());
pp.ProcessPayment();
}
Комментарии:
1. Я согласен, эталонное решение лучше и более четко выражает то, что здесь происходит.
2. На самом деле, в текущей настройке вам даже не понадобится динамическое выделение для
MyClass
‘screditCardService_
, но я предполагаю (или, скорее, надеюсь), что реальный код немного сложнее.3. @User: Нет. Вы можете объявить его как локальную переменную внутри
DoIt
функции.PaymenProcessor
все равно нужно будет использовать ссылку на базовый класс, и все его вызовы методов будут по-прежнему динамическими. Но вDoIt
unique_ptr
том коде, который вы показали, вам вообще не нужно использовать a. Я ожидаю, что ваш реальный код более сложный.4. @User Вы можете просто создать
VisaCardService
переменную-член вMyClass
(без указателя) или, что еще лучше, локальнуюVisaCardService
переменнуюDoIt
. Всегда имейте в виду, что это C , где автоматические переменные могут (и должны, когда это возможно) использоваться, и что полиморфизм не обязательно подразумевает динамическое распределение. Тот факт, чтоPaymentProcessor
принимает указатель (или, лучше, ссылку), не означает, что это хранилище должно выделяться динамически. Но, конечно, если реальный код более сложный, вам все равно может потребоваться динамически выделяемый объект.5. @User Да, конечно, до тех пор, пока
PaymentProcessor
принимает (и сохраняет) ссылку или указатель.