多态(继承)和值类型

jdm*_*jdm 4 c++ polymorphism value-type

我有一堆的类型,PixelMeasure,PointMeasure,CentimeterMeasure等等,这表示与一个单元的值.我希望他们有

  • 值语义:例如有效不可变,不必担心内存分配,以及
  • 多态性:我可以返回一个类型的对象,Measure并且可以对它进行操作,而不知道它是什么类型的.我还希望能够将多个不同的Measures放入容器中.

看来这些在C++中是互斥的.对于多态,我需要使用指针或引用.

我看到两个选择:

  • 使用智能指针,例如shared_ptr.这给了我想要的行为(安全,没有原始指针,但是多态调度).缺点是:
    • 它很冗长(如果我真的想要,我可以将它隐藏在typedef后面).
    • 你有内存分配(但代码不是性能关键,而是隐藏起来).
    • 语义很奇怪 - 我的对象(shared_ptr<PixelMeasure>)的副本将共享相同的底层指针.我仍然可以假装它具有值语义 - 如果我使接口不可变,那应该没关系.
  • 我想简单说一下不使用继承(没有共同的基类),并通过模板调度 - 但在这种情况下,我需要知道确切的措施那种在编译的时候,不能把它们放到容器中.
  • 我可以摆脱类的干脆只使用一个类,使用值和单位场 - 但是这将是少了很多灵活,使用语法会更糟糕,所以我宁愿避免这种情况.

有任何想法吗?

Jen*_*ens 7

你可以使用type-erase,因为正如Sean Parent所说,继承是所有邪恶的基类.他还有一个演示价值语义和基于概念的多态性,这可能是你想要的.这背后是同样的想法std::function.

基本上,您通过内部类中的继承使用子类型多态,以使用以多态方式映射到概念的所有内容.以下是带有Merged Concepts的Type Erasure示例:

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_;
};
Run Code Online (Sandbox Code Playgroud)

现在,您可以将所有内容放入具有greet方法的Greeter对象中.其他示例是boost :: any_iterator或std :: function.

每个Measure值将遭受一次内存分配.


小智 0

您可以使用带有适当复制构造函数的包装类和指向您的度量的指针作为字段。您可能需要向 Measure 添加克隆方法。

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;
};
Run Code Online (Sandbox Code Playgroud)