Копируемый класс экземпляра
class object
{
public:
object(dependency d) : dep_(d) {}
private:
dependency dep_;
};
Работает только в том случае, если класс dependency
полностью не имеет состояния, т.е. не имеет членов. На практике это случается редко, потому что класс dependency
может хранить свою собственную зависимость.
Необработанный указатель
class object
{
public:
object(dependency *d) : dep_(d)
{
if (d == nullptr)
throw std::exception("null dependency");
}
private:
dependency *dep_;
};
Это работает как настоящая инъекция. Нам необходимо проверить переданный указатель на значение nullptr
.
Класс object
не владеет классом dependency
, поэтому вызывающий код обязан убедиться, что object
уничтожен до объекта dependency
.
В реальном приложении иногда очень сложно проверить.
Ссылка
#define DISALLOW_COPY_AND_ASSIGN(Class) \
Class(const Class &) = delete; \
Class &operator=(const Class &) = delete
class object
{
public:
object(dependency &d) : dep_(d) {}
DISALLOW_COPY_AND_ASSIGN(object);
private:
dependency &dep_;
};
Ссылка не может быть нулевой, поэтому в этой перспективе это немного безопаснее.
Однако этот подход накладывает дополнительные ограничения на класс object
: он не должен быть копируемым, поскольку ссылка не может быть скопирована. Вам нужно либо вручную переопределить оператор присваивания и конструктор копирования, чтобы остановить копирование, либо унаследовать его от чего-то вроде boost::noncopyable
.
Как и в случае с необработанным указателем, существует ограничение владения. Вызывающий код должен обеспечивать правильный порядок уничтожения для обоих классов, иначе ссылка станет недействительной и приложение выйдет из строя с нарушением прав доступа.
Если зависимость является константной ссылкой:
class object
{
public:
object(const dependency &d) : dep_(d) {}
private:
const dependency &dep_;
};
стоит обратить внимание на то, что класс object
принимает ссылки на временные объекты:
dependency d;
object o1(d); // this is ok, but...
object o2(dependency()); // ... this is BAD.
Дальнейшие подробности:
Умный указатель
class object
{
public:
object(std::shared_ptr<dependency> d) : dep_(d)
{
if (!d)
throw std::exception("null dependency");
}
private:
std::shared_ptr<dependency> dep_;
};
Подобно необработанному указателю, но право собственности контролируется механизмом интеллектуального указателя.
По-прежнему нужно проверить nullptr
в теле конструктора.
Основным преимуществом является контроль времени жизни объекта dependency
: вызывающему приложению не нужно должным образом контролировать порядок уничтожения (но учтите, что вам нужно будьте очень осторожны при разработке API с std::shared_ptr
).
Когда класс dependency
больше не используется, он автоматически уничтожается shared_ptr
деструктором.
Бывают случаи, когда shared_ptr
принадлежащие объекты не уничтожаются (так называемые циклические ссылки). Однако при внедрении конструктора циклические зависимости невозможны из-за конкретного четко определенного порядка построения.
Это, конечно, работает, если в приложении не используются другие методы инъекции.
Умный указатель имеет небольшие накладные расходы, но в большинстве случаев это не проблема.
Дальнейшие подробности:
person
Community
schedule
01.09.2016