c Могу ли я использовать std ::unique_ptr с внедрением зависимостей?

#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 ‘s creditCardService_ , но я предполагаю (или, скорее, надеюсь), что реальный код немного сложнее.

3. @User: Нет. Вы можете объявить его как локальную переменную внутри DoIt функции. PaymenProcessor все равно нужно будет использовать ссылку на базовый класс, и все его вызовы методов будут по-прежнему динамическими. Но в DoIt unique_ptr том коде, который вы показали, вам вообще не нужно использовать a. Я ожидаю, что ваш реальный код более сложный.

4. @User Вы можете просто создать VisaCardService переменную-член в MyClass (без указателя) или, что еще лучше, локальную VisaCardService переменную DoIt . Всегда имейте в виду, что это C , где автоматические переменные могут (и должны, когда это возможно) использоваться, и что полиморфизм не обязательно подразумевает динамическое распределение. Тот факт, что PaymentProcessor принимает указатель (или, лучше, ссылку), не означает, что это хранилище должно выделяться динамически. Но, конечно, если реальный код более сложный, вам все равно может потребоваться динамически выделяемый объект.

5. @User Да, конечно, до тех пор, пока PaymentProcessor принимает (и сохраняет) ссылку или указатель.