【问题标题】:C++ Make a generic interfaceC++ 创建一个通用接口
【发布时间】:2014-02-21 10:45:14
【问题描述】:

我有一个接口(缺少很多成员,但请注意这个接口是强制性的)。我将需要 5 个继承自它的类,它们将具有 _value 属性。所以,ins\ 实现了 5 个类(对于 char、short、int、float、double),我想到了一个模板类:

class my_interface
{
public:
    virtual [various_types]    getValue() const = 0;
};

template<typename T>
class my_class : public my_interface
{
private:
    T    _value;
public:
    my_class(T value) : _value(value) {} // initialize the attribute on construct
    virtual T        getValue() const { return _value; }
};

...这样类似的东西可以工作:

void                my_function()
{
    my_inteface*    a = new my_class<char>(42);
    my_interace*    b = new my_class<short>(21);
    int             result;

    result = a->getValue() + b->getValue();
}

但我不知道我该怎么做。看来您不能在纯虚拟界面上制作模板。对我来说,唯一可行的方法是让 getValue() 始终返回双精度值,因为它是我需要的最大尺寸类型。但是,我不喜欢这种解决方案。

【问题讨论】:

  • 您需要对您的类型进行所有算术运算还是受到限制?你总是有不知道子类的实际类型的问题。
  • 你的问题是你根本不能有像[various_types]这样的虚函数的返回类型,除非你把my_interface也做成一个模板类。
  • 这是不可能的,因为 C++ 是一种静态类型语言,返回类型不能从动态类型派生。您可能想查看 Boost.Variant 和/或 Boost.Any 以了解类似问题的实现。然而,没有人能说它是否是你需要的,因为你没有告诉我们这背后的原始问题:告诉我们你想要实现的什么,而不是如何你尝试解决它。
  • @MatthiasBonora:对不起,我需要一个接口,而不是抽象类,事实上,你明白,我说的是算术运算符重载。所以是的,我需要所有的算术运算。

标签: c++ interface polymorphism


【解决方案1】:

如果您的接口上只有一种方法 (getValue()),那么您只需要模板类实现。

但是,如果你想要这样的界面:

std::string getValue();
int getValue();
long getValue();

那么你就不走运了,因为你不能只根据返回类型重载函数名。或者,您可以创建一个包装器类型。

编辑

我所说的包装类型是指,如果需要 getValue 来返回多种类型,您可以使用包装类来封装所需功能,而不是将其添加到顶级接口中,以多种方式实现。它可能看起来像这样:

enum ValType{
  INT, CHAR, STR, DEQUE
};

class Wrapper{
private:
  union S{
    int intVal;
    char charVal;
    std::string stringVal;
    std::deque dequeVal;
    ~S() {}
  } theVal;

  ValType heldType;
public:
  void setVal(int value){ heldType = INT; theVal.intVal = value; }
  void setVal(char value){ heldType = CHAR; theVal.charVal = value; }
  // ... and so on
  int getIntVal() const {
    if(heldType!=INT) 
      throw std::runtime_error("Cop on");
    return theVal.int;
  }
  // and so on
}

那么你的界面就是

public class my_interface{
  virtual Wrapper getVal();
}

你在这里并没有真正获得太多,因为用户仍然需要调用 Wrapper 的正确子成员。 如果需要,您也可以将返回值表示为字符串。

请注意,使用联合时需要注意以下警告: http://en.cppreference.com/w/cpp/language/union

编辑 2: 您可以使用模板化返回来做到这一点

template<typename = T>
const T& getVal(const T& typeToAllowMethodOverriding) const;

【讨论】:

  • 不,我说该接口中还有其他方法。但是你所说的包装类型是什么意思?
  • 哦,好的,我明白你在这里做了什么。但是正如您所说,获取值与类模板一样困难......事实上,我认为最好的做法是返回一个包含该值的模板化对象。但我也有同样的麻烦:我的界面不能有返回模板类型的方法...
  • 是的,您可以返回模板类型,但前提是您在调用它时知道该类型。您仍然需要提供一种机制来正确覆盖该方法(使用参数)。见编辑 2
【解决方案2】:

请记住,'virtual ...' 的意思是“在类中创建一个函数指针,我将使用它来调用正确的派生类”。它只是一个指针——所以你必须在调用它之前就返回类型达成一致。 C++ 没有其他语言的动态类型和反射——它必须在调用之前知道返回类型是什么。

【讨论】:

    【解决方案3】:

    您也可以将 my_interface 类设为模板类:

    template<typename T>
    class my_interface
    {
    public:
        virtual T getValue() const = 0;
    };
    

    这将为您使用的每个类型 T 生成一个接口和一个类。

    【讨论】:

    • 这行得通吗? my_interface 和 my_interface 将是不同的类型,从而消除了接口的点
    • 只有当你有多个相同类型的子类时,但如果每种类型只有一个子类,那么这不是一个真正可行的解决方案
    【解决方案4】:
        template<typename U>
    class my_interface
        {
        public:
    
            virtual  U  getValue() const = 0;
        };
    
        template<typename T>
        class my_class : public my_interface<T>
        {
        private:
            T    _value;
        public:
            my_class(T value) : _value(value) {} // initialize the attribute on construct
    
               T getValue() const { return  _value; }
        };
    

    它适用于所有类型,但对于字符串类型,您必须对模板进行专门化

    【讨论】:

      【解决方案5】:

      我认为你最好的办法是使用/实现类似boost::variant的东西。

      typedef boost::variant<char, short, int, float, double> Value;
      
      class my_interface
      {
      public:
          virtual Value getValue() const = 0;
      };
      
      template<typename T>
      class my_class : public my_interface
      {
      private:
          T    _value;
      public:
          my_class(T value) : _value(value) {} // initialize the attribute on construct
          virtual Value getValue() const { return _value; }
      };
      

      然后你可以实现算术运算符:

      Value operator+(const Value &lhs, const Value &rhs) {
          //Check the internal types of lhs and rhs and perform the operation
      }
      

      您还可以将boost::variant 包装在另一个类中,以提供基本类型的转换运算符。

      【讨论】:

        猜你喜欢
        • 2019-01-26
        • 1970-01-01
        • 1970-01-01
        • 2023-03-07
        • 1970-01-01
        • 1970-01-01
        • 2019-06-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多