多态性(继承)和价值类型



我有很多类型的 PixelMeasurePointMeasureCentimeterMeasure等,代表带有单元的值。我希望他们有

  • 价值语义:例如有效不变,不必担心内存分配,
  • 多态性:我可以返回类型Measure的对象,并且可以在它上进行操作,而无需知道它是什么特定种类。我也希望能够将多个不同的Measure S放入容器中。

看来这些在C 中是相互排斥的。对于多态性,我需要使用指针或参考。

我看到了两个选项:

  • 使用智能指针,例如shared_ptr。这给了我想要的行为(安全,没有原始的指针,但多态调度)。缺点是:
    • 它是冗长的(如果我真的想要的话,我可以将其隐藏在Typedef后面)。
    • 您的内存分配在引擎盖下方(但代码不是至关重要的,它被隐藏了)。
    • 语义是Wierd的 - 我的对象(shared_ptr<PixelMeasure>)的副本将共享相同的基础指针。我仍然可以假装它具有价值语义 - 如果我使接口不变,那就没关系了。
  • 我简短考虑了不使用继承(没有共同基类)并通过模板进行调度 - 但是在这种情况下,我需要在编译时知道确切的措施类型,并且不能将它们放入容器中。
  • 我可以完全摆脱课程,只使用一个具有价值和单元字段的类,但这会较小的灵活性,并且使用语法会更糟,所以我宁愿避免这种情况。

有什么想法?

您可以使用Type-erase,因为正如Sean父母所说,继承是所有邪恶的基础。他还具有基于概念的多态性语义和基于概念的多态性,这可能是您想要的。这是相同的想法,例如std::function

基本上,您通过内部类中的继承使用子类型多态性,以将所有这些映射到概念多态态的所有内容。这是带有合并概念的类型擦除的示例:

class Greeter {
  public:
    // Constructor: We can stuff anything into a Greeter costume.
    template <class T>
    Greeter(T data) : self_(std::make_shared<Model<T>>(data)) {}
    // External interface: Just forward the call to the wrapped object.
    void greet(const std::string &name) const {
        self_->greet(name);
    }
  private:
    // The abstract base class is hidden under the covers...
    struct Concept {
        virtual ~Concept() = default;
        virtual void greet(const std::string &) const = 0;
    };
    // ... and so are the templates.
    template <class T>
    class Model : public Concept {
      public:
        Model(T data) : data_(data) {}
        virtual void greet(const std::string &name) const override {
            // Forward call to user type.
            // Requires that T can greet.
            data_.greet(name);
        }
      private:
        // The user defined Greeter will be stored here. (by value!)
        T data_;
    };
    // Polymorphic types require dynamic storage.
    // Here we store our pointer to the Model that holds the users Greeter.
    std::shared_ptr<const Concept> self_;
};

现在,您可以将所有内容都放入具有问候方法的迎接器对象中。其他示例是boost :: any_iterator或std :: function。

您将遭受每个测量值的记忆分配。

您可以使用带有适当复制构建器的包装类别和指针作为字段。您可能需要添加克隆方法进行测量。

class MeasureWrapper
{
public: 
    MeasureWrapper(const MeasureWrapper &measureToCopy)
    {
        m_measure = measureToCopy.m_measure->Clone();
    }
    MeasureWrapper(Measure *measure) : m_measure(measure)
    {
    }
    ~MeasureWrapper()
    {
        delete m_measure;
    }
    // Wrap Measure interface here and call m_measure methods...       
private:
    Measure *m_measure;
};

您可以为此使用变体类型:它避免了动态分配,但是使多态性调度更为复杂。

请参阅boost.variant,希望有一个标准版本。

另外,您可以编写一个更具体的区分联合,提供一个很好的特定多态性界面

最新更新