类的抽象成员应该是指针还是引用

Should abstract members of a class be pointers or references?

本文关键字:指针 引用 抽象 成员      更新时间:2023-10-16

类的抽象成员应该是指针还是引用?

举个玩具的例子,假设我有以下类:

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不拥有这些,不应删除它们。

这些类成员应该是指针还是引用?或者这种模式应该完全不同吗?

这几乎是依赖注入(/inversation)的hello-world示例。

这里有一个类似的问题,有不同的解决方案:在没有原始指针的C++11中进行依赖注入

编辑:我把原来的答案从这里移到了那个问题上。


SerializedFileProcessor示例的原始答案:

只使用指针(智能或原始),甚至是普通C++引用的缺点是,它们允许从const上下文调用非常量方法。

我提出了一个类似但不完全相同的包装器std::reference_wrapper(它缺少const-safe访问器)。将T*替换为unique_ptrshared_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; 
};

它们可以是任意一种,您必须只确保引用的对象在使用前不会消亡(即,通常其生存期应超过SerializedFileProcessor生存期)。

引用成员有一个缺点,那就是几乎不可能创建赋值运算符(所以如果你需要一个,那么你就需要一个指针——即使对于有引用成员的类,也有一些技术可以进行赋值,但它们非常糟糕)。此外,如果它们是可选的,那么它们需要是指针(引用不是可选的)。

如果您的类不拥有它们,那么它就不应该是引用。使用指针并让所有者持有std::unique_ptr