为具有特定成员变量值的类创建不同类型

时间:2017-11-29 19:40:01

标签: c++ c++11 inheritance types

给定一个具有定义类类型的枚举的类,如下例所示:

class Fruit {
 public:

   enum class FruitType {
      AppleType = 0,
      OrangeType = 1,
      BananaType = 2,
   };
   Fruit(FruitType type) : type_(type) {}
   FruitType fruit_type() const { return type_; }

 private:
   FruitType type_;
};

和从它派生的类共享相同的枚举:

class DriedFruit : public Fruit {
 public:
  // Some Dried specific methods.
};

是否有可能以某种方式为每个特定的枚举值定义Fruit和DriedFruit的不同类型:

class Apple   // Fruit with FruitType = AppleType
class Orange  // Fruit with FruitType = OrangeType
class Banana  // Fruit with FruitType = BananaType
class DriedApple   // DriedFruit with FruitType = AppleType
class DriedOrange  // DriedFruit with FruitType = OrangeType
class DriedBanana  // DriedFruit with FruitType = BananaType

因此3类Apple,Orange和Banana是不同的类型,3类DriedApple,DriedOrange,DriedBanana是不同的类型。

我的问题有点类似于How to define different types for the same class in C++,除了我想在类中将类类型的信息显式存储为枚举成员变量,并为所有不同类型提供公共基类。

最有效的方法是什么?

编辑: 主要的用例如下 - 在我的应用程序中,有些方法只是期望Apple作为输入,或者只是期望Orange作为输入,而且许多方法都不关心它是什么。

将Fruit传递给只需要Apple的方法感觉不安全/模糊,同时有许多方法不关心它是哪种类型因此有3种不同的类型也不是一个好的选择。

主要工作流程如下: 然后从一些输入参数构建一个Fruit 传递它并将其作为水果处理,然后在某个时刻 如果它是Apple,可以从Fruit转换为具体的Apple类型,并进一步处理它,从那时起将其类型限制为Apple。

3 个答案:

答案 0 :(得分:3)

  

最有效的方法是什么?

您可以使用非类型模板参数:

enum class FruitType {
   AppleType = 0,
   OrangeType = 1,
   BananaType = 2,
};

template <FruitType F>
class Fruit {
 public:
   FruitType fruit_type() const { return F; }
};

using Apple = Fruit<FruitType::AppleType>;
using Banana = Fruit<FruitType::BananaType>;

您是否需要实际的基类取决于您。为某些FruitType提供模板特化也可能就足够了。

答案 1 :(得分:2)

这是你想做的吗?

map.UserLocationTouch = false 

打印:

enum class FruitType 
{
    AppleType = 0,
    OrangeType = 1,
    BananaType = 2,
};

class Fruit 
{
public:

    virtual FruitType fruit_type() const = 0;
};

class Apple: public Fruit 
{
public:

    FruitType fruit_type() const override { return FruitType::AppleType; }
};

class Orange : public Fruit 
{
public:

    FruitType fruit_type() const override { return FruitType::OrangeType; }
};

class Banana : public Fruit 
{
public:

    FruitType fruit_type() const override { return FruitType::BananaType; }
};

int main()
{
    Fruit *somefruit = new Apple;

    std::cout << "Is Apple? " << std::boolalpha << (somefruit->fruit_type() == FruitType::AppleType) << std::endl;
    std::cout << "Is Orange? " << std::boolalpha << (somefruit->fruit_type() == FruitType::OrangeType) << std::endl;
    std::cout << "Is Banana? " << std::boolalpha << (somefruit->fruit_type() == FruitType::BananaType) << std::endl;

    return 0;
}

答案 2 :(得分:0)

关于要求,你的问题非常抽象。

虽然您的编辑说明指明了一种方式

  

主要用例如下 - 在我的应用程序中,某些方法只是期望Apple作为输入,或者仅仅期望Orange作为输入,并且许多方法不关心它是什么。

我正在考虑基于接口和标记接口的完全不同的系统
(参见完整的Live Demo)。

首先为所有水果定义一个通用界面:

// A basic interface common for all fruits
struct IFruit {
  virtual ~IFruit() {}
  virtual std::string category() const = 0;
  virtual std::string common_name() const = 0;
  virtual std::string botanical_name() const = 0;
};

// An overload for the output operator is just nifty
std::ostream& operator<<(std::ostream& os, const IFruit& fruit) {
    os << "Category       : " << fruit.category() << std::endl;
    os << "Common Name    : " << fruit.common_name() << std::endl;
    os << "Botanical Name : " << fruit.botanical_name() << std::endl;
    return os;
}

定义标记接口以区分您的特定类型(苹果,橘子):

// Tag interfaces to distinguish (not necessarily empty)
struct IApple : public IFruit {
  virtual ~IApple() {}
};

struct IOrange : public IFruit {
  virtual ~IOrange () {}
};

这些应该要求隐式实现IFruit接口。

现在您可以提供一个抽象基类,它实现IFruit接口 这个基类是 abstract ,从某种意义上说构造函数是从public范围隐藏的,需要由继承类构造函数调用:

// Abstract base class implementation
template<class TagInterface>
class FruitBase : public TagInterface {
protected:
      std::string category_;
      std::string common_name_;
      std::string botanical_name_;

      FruitBase ( const std::string& category
                , const std::string& common_name
                , const std::string botanical_name) 
          : category_(category), common_name_(common_name)
          , botanical_name_(botanical_name) 
      {}

public:
      virtual ~FruitBase () {}
      virtual std::string category() const { return category_; }
      virtual std::string common_name() const { return common_name_; }
      virtual std::string botanical_name() const { return botanical_name_; }
};

根据需要添加其他标记接口

 struct IDriedApple : public IApple {
     virtual ~IDriedApple() {}
     virtual int rest_humidity() const = 0;
 };

现在,您可以使用非常狭窄的类定义创建具体实现:

// Concrete apples
struct Boskop : public FruitBase<IApple> {
public:
     Boskop() : FruitBase("Apples","Boskop","Malus domestica 'Belle de Boskoop'") {}
};

struct Braeburn : public FruitBase<IApple> {
public:
     Braeburn() : FruitBase("Apples","Braeburn","Malus domestica") {}
};

// Concrete oranges
struct Valencia : public FruitBase<IOrange> {
public:
     Valencia() : FruitBase("Oranges","Valencia","Citrus × sinensis") {}
};

struct Navel : public FruitBase<IOrange> {
public:
     Navel() : FruitBase("Oranges","Navel","Citrus × sinensis") {}
};

这里我假设的是专门用于 Apples Oranges 的函数声明:

void aFunctionThatTakesOnlyApples(IApple& anApple) {
    std::cout << "This is an apple:" << std::endl;
    std::cout << anApple;
}

void aFunctionThatTakesOnlyOranges(IOrange& anOrange) {
    std::cout << "This is an orange:" << std::endl;
    std::cout << anOrange << std::endl;
}

这是一个简单的模板函数,用于查询IFruit的已知实例,以实现特定的标记接口:     模板     TagInterface * queryTagInterface(IFruit * fruit){         return dynamic_cast(fruit);     }

这就是你如何使用所有这些:

int main() {
    std::vector<std::unique_ptr<IFruit>> allFruits;
    allFruits.push_back(std::make_unique<Boskop>());
    allFruits.push_back(std::make_unique<Braeburn>());
    allFruits.push_back(std::make_unique<Valencia>()); 
    allFruits.push_back(std::make_unique<Navel>());
    for(auto& fruit : allFruits) {
        if(IApple* anApple = queryTagInterface<IApple>(fruit.get())) {
            aFunctionThatTakesOnlyApples(*anApple);
        }
        if(IOrange* anOrange = queryTagInterface<IOrange>(fruit.get())) {
            aFunctionThatTakesOnlyOranges(*anOrange);
        }
        std::cout << "-----------------------------------------------" << std::endl;
    }    
}
  

将Fruit传递给只需要Apple的方法感觉不安全/模糊,同时有许多方法不关心它是哪种类型因此有3种不同的类型也不是一个好的选择。

我应该注意到,我还是不明白是什么让 Apples Oranges 让不同的 Fruits 得到他们真正应得的自己的类型。但是,这可能适用于不那么抽象的多态类设计隐喻,对于具体的类层次结构设计很有用。