C++ 多态性(继承)和值类型

C++ 多态性(继承)和值类型,c++,polymorphism,value-type,C++,Polymorphism,Value Type,我有一系列类型,pixelmease,PointMeasure,cm measure等等,它们用一个单位表示一个值。我希望他们有 值语义:例如,有效地不可变,不必担心内存分配,以及 多态性:我可以返回类型为Measure的对象,并且可以在不知道它是什么具体类型的情况下对其进行操作。我还希望能够将多个不同的Measures放入一个容器中 这些在C++中似乎是互斥的。对于多态性,我需要使用指针或引用 我看到两种选择: 使用智能指针,例如共享\u ptr。这给了我想要的行为(安全,没有原始指针,

我有一系列类型,
pixelmease
PointMeasure
cm measure
等等,它们用一个单位表示一个值。我希望他们有

  • 值语义:例如,有效地不可变,不必担心内存分配,以及
  • 多态性:我可以返回类型为
    Measure
    的对象,并且可以在不知道它是什么具体类型的情况下对其进行操作。我还希望能够将多个不同的
    Measure
    s放入一个容器中
<>这些在C++中似乎是互斥的。对于多态性,我需要使用指针或引用

我看到两种选择:

  • 使用智能指针,例如
    共享\u ptr
    。这给了我想要的行为(安全,没有原始指针,但是多态调度)。缺点是:
    • 它是冗长的(如果我真的想要的话,我可以把它藏在typedef后面)
    • 内存分配在幕后进行(但代码不是性能关键的,它被隐藏起来)
    • 语义是wierd-我的对象的副本(
      shared_ptr
      )将共享相同的底层指针。我仍然可以假装它具有值语义——如果我使接口不可变,那就没关系了
  • 我简单地考虑过不使用继承(没有公共基类)和通过模板进行分派,但在这种情况下,我需要在编译时知道确切的度量类型,并且不能将它们放入容器中
  • 我可以完全去掉这些类,只使用一个类,带有一个值和一个单位字段——但这样灵活性会低很多,使用语法也会更差,所以我宁愿避免这样做

有什么想法吗?

您可以使用一个具有适当复制构造函数的包装类,并将指向度量值的指针作为字段。您可能需要添加克隆方法来测量

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;
};

您可以为此使用变体类型:它避免了动态分配,但使多态分派更加复杂

看,希望有一个标准版本即将问世


或者,您可以编写一个更具体的区分并集,提供一个漂亮的特定多态样式接口

您可以使用类型擦除,因为正如Sean Parent所说。他也有一个演讲,这可能是你想要的。例如,
std::function
背后的想法也是一样的

基本上,通过内部类中的继承使用子类型多态性,以多态方式使用映射到概念的所有内容。以下是一个例子:

类迎宾员{
公众:
//建造师:我们可以把任何东西塞进迎宾服。
模板
问候者(T数据):自我(std::使_共享(数据)){
//外部接口:只需将调用转发到包装对象。
void greet(const std::string&name)const{
自我问候(姓名);
}
私人:
//抽象基类隐藏在下面。。。
结构概念{
virtual~Concept()=默认值;
虚拟空(const std::string&)const=0;
};
//…模板也是如此。
模板
班级模式:公共概念{
公众:
模型(T数据):数据{(数据){
虚拟void greet(常量std::string&name)常量重写{
//向用户类型转发调用。
//要求T能打招呼。
数据(名称);
}
私人:
//用户定义的迎宾员将存储在此处。(按值!)
T数据;
};
//多态类型需要动态存储。
//在这里,我们存储指向容纳用户问候者的模型的指针。
std::共享的ptr自身;
};
现在,您可以将所有内容放入具有greet方法的greet对象中。其他示例包括boost::any_迭代器或std::函数


每个度量值将有一个内存分配。

如何?用户定义的文本将为我提供一种在代码中使用它们的好方法,但它是1。只有句法上的糖,底层的表示是独立的,2。我很少在代码中创建这些实例。它们是从配置文件中解析出来的。例如,我所做的是将它们转换为像素,或者按原样显示它们。还有一些更复杂的单位,例如窗高百分比,它引用了一个窗?或者更好,有一个变体?谢谢,这是相当优雅的,特别是从使用方面。它让我想起了一些类型类。然而,仅仅使用智能指针作为包装器(除了隐藏智能指针之外)有什么优势?我担心这可能有点“聪明”,人们不会理解
迎宾员
英语
之间的关系(而
共享
共享
是丑陋的,但很清楚)。当你有多个概念时,最后一个链接中的技巧当然很好。@jdm如果你使用一个简单的智能指针,你需要有一个通用的基类,包含你在这里输入的所有内容。使用类型擦除,您可以使用实现该概念的任何东西,即具有相同语法接口的任何东西。例如,如果您有一个
std::function
,则可以指定一个函数、一个lambda表达式甚至一个Functor对象。
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_;
};