联盟成员根据不同类型专门在一个班级

时间:2016-06-03 19:16:32

标签: c++ class unions

我正在实现一个类,其中每个实例都有一个句柄,该句柄将完全依赖于成员type。不同的类型与不同的句柄相关联。

enum class Type {
  TypeA, TypeB, TypeC;
};

class HandleA { ... };
class HandleB { ... };
class HandleC { ... };

class Foo {
  public:
    Type type;
    std::unique_ptr<HandleA> handleA;
    std::unique_ptr<HandleB> handleB;
    std::unique_ptr<HandleC> handleC;
};

理想情况下,如果类型为handleA,则存在TypeA,如果类型为handleB,则存在TypeB,如果类型为{{handleC,则存在TypeC 1}}。但是,我认为应该有一个更好的设计模式。否则,我拥有的类型越多,unique_ptr在类Foo中占用的union就越多。我知道c++可能是一个很好的解决方案,但我想知道是否存在可扩展性更高的c++11self.get_queryset()解决方案。由于Foo可能以虚拟方式用于其他派生类,这将采用不同的构造函数,我不是在寻找模板解决方案。

1 个答案:

答案 0 :(得分:1)

我一直在讨论这些要求,并发现这里要解决的基本问题是:如何在运行时选择类型。

其他所有东西都可以通过一些模板魔法来解决 - 在Foo本身不是模板的情况下会失败。

任何克服这个问题的技巧,然后,(我们可以用成员函数指针做一些惊人的事情)似乎只是为了重新实现VTable间接虚拟函数已经给我们免费提供给我们。在这种考虑下,我倾向于遵循@RobK的评论:

#include <iostream>
#include <memory>
#include <string>

enum class Type { TypeA, TypeB, TypeC };

class Handle
{
public:
  // virtual ~Handle() = default; // if any subclass is not empty
  virtual std::string sing() const = 0; // purely virtual function
};
class HandleA : public Handle { public: std::string sing() const { return "Aaha"; } };
class HandleB : public Handle { public: std::string sing() const { return "Behe"; } };
class HandleC : public Handle { public: std::string sing() const { return "Cehe"; } };

class Foo
{
  Type type;
  std::unique_ptr<Handle> handle;

public:
  constexpr Foo(Type newType) : type{newType}, handle()
  {
    switch(newType) // establishes the mapping between Type and Handle-subclass
    {
      case Type::TypeA: this->handle.reset(new HandleA()); return;
      case Type::TypeB: this->handle.reset(new HandleB()); return;
      case Type::TypeC: this->handle.reset(new HandleC()); return;
    }
  }

  // virtual function call automatically resolves to used subclass
  void showHandle() const { std::cout << this->handle->sing() << std::endl; }
};

int main()
{
  Foo fooA(Type::TypeA);
  Foo fooB(Type::TypeB);
  Foo fooC(Type::TypeC);

  fooA.showHandle();
  fooB.showHandle();
  fooC.showHandle();
}

请注意,以这种方式使用不完整类型(例如我的句柄)时,请注意以下几点:

  1. 如果任何子类定义了自己的非静态成员对象,则Handle和所有子类必须接收声明的虚拟析构函数。
    • 如果没有,这种子类的对象的破坏是不完整的,并且会泄漏内存。
    • 就像Handle中注释掉的析构函数一样,那些只能是默认的。
  2. 不覆盖我的Handle :: sing之类的纯虚函数,然后调用它会立即终止该程序。