Должны ли абстрактные члены класса быть указателями или ссылками?

Должны ли абстрактные члены класса быть указателями или ссылками?

Для игрушечного примера скажем, что у меня есть следующий класс:

class SerializedFileProcessor {
public:
    std::string Process(std::string file) const {
        std::string text = deserializer.Deserialize(file);
        text = processor.Process(text);
        return serializer.Serialize(text);
    }

private:
    IDeserializer? deserializer;
    IProcessor? processor;
    ISerializer? serializer; 
};

Где (экземпляры конкретных подклассов) десериализатор, процессор и сериализатор передаются в конструктор этого класса.

SerializedFileProcessor не владеет ими и не должен их удалять.

Должны ли эти члены класса быть указателями или ссылками? Или этот узор надо делать совсем по другому?


person Mistodon    schedule 16.12.2015    source источник
comment
Я не вижу в этом ничего общего с абстрактностью.   -  person djechlin    schedule 16.12.2015
comment
Я могу ошибаться, но поскольку тип IWhatever абстрактный, данные не могут быть скопированы в SerializedFileProcessor. Это единственная причина, по которой я указал, что это абстрактные классы.   -  person Mistodon    schedule 16.12.2015
comment
Если вы можете использовать ссылки, всегда предпочитайте их указателям.   -  person R Sahu    schedule 16.12.2015
comment
Это зависит от того, что остается в них постоянным после построения SerializedFileProcessor. Если тип и местоположение не могут измениться, то ссылка, скорее всего, в порядке, а если ссылка в порядке, то, вероятно, лучше. Если тип объекта может измениться, то его адрес должен измениться, и он не может быть ссылкой.   -  person JSF    schedule 16.12.2015


Ответы (3)


Это в значительной степени приветственный пример внедрения зависимостей (/inversion).

Здесь есть аналогичный вопрос с разными решениями: Внедрение зависимостей в С++ 11 без необработанные указатели

Изменить: я переместил свой первоначальный ответ отсюда на этот вопрос.


Оригинальный ответ с SerializedFileProcessor примером:

Недостатком простого использования указателя (интеллектуального или необработанного) или даже обычной ссылки на C++ является то, что они позволяют вызывать неконстантные методы из константного контекста.

Я предлагаю оболочку, похожую, но не идентичную std::reference_wrapper (в которой отсутствует безопасный метод доступа const). Замените T* на unique_ptr или shared_ptr, чтобы получить собственные версии (также добавьте конструкцию перемещения по умолчанию).

  template<typename T>
  struct NonOwningRef{
    NonOwningRef() = delete;
    NonOwningRef(T& other) noexcept : ptr(std::addressof(other)) { };
    NonOwningRef(const NonOwningRef& other) noexcept = default;

    const T& value() const noexcept{ return *ptr; };
    T& value() noexcept{ return *ptr; };

  private:
    T* ptr;
  };

Применение:

class SerializedFileProcessor {
public:
    std::string Process(std::string file) const {
        std::string text = deserializer.value().Deserialize(file);
        text = processor.value().Process(text);
        return serializer.value().Serialize(text);
    }

private:
    NonOwningRef<IDeserializer> deserializer;
    NonOwningRef<IProcessor> processor;
    NonOwningRef<ISerializer> serializer; 
};
person Johan Lundberg    schedule 16.12.2015
comment
Спасибо! Это действительно полезная ссылка. Я не знаю, почему мне не пришло в голову направить мой вопрос больше на DI. - person Mistodon; 16.12.2015

Они могут быть любыми, вы должны только убедиться, что объекты, на которые ссылаются, не умирают до их использования (т.е. в целом их время жизни должно превышать время жизни SerializedFileProcessor).

У ссылочных членов есть один недостаток — в этом случае практически невозможно создать оператор присваивания (поэтому, если он вам нужен, вам нужен указатель — существуют методы фактического присваивания даже для классов со ссылочными членами, но они очень неприятны). ). Также, если они необязательны, они должны быть указателями (ссылка не является обязательной).

person axalis    schedule 16.12.2015

Если они не принадлежат вашему классу, это не должно быть ссылкой. Используйте указатель и попросите владельца удерживать std::unique_ptr.

person fuzzything44    schedule 16.12.2015
comment
Если они не принадлежат вашему классу, это не должно быть ссылкой. Это непонятно/неправильно. Ссылки не принадлежат в C++. - person Johan Lundberg; 16.12.2015
comment
На самом деле все наоборот, если он владеет, это должен быть указатель (необработанный или умный) - person Slava; 16.12.2015