"Pure virtual variable" 或者:如何强制派生class 初始化一个静态成员变量?

"Pure virtual variable" OR: how to force derived class to init a static member variable?

我有一个单例 class Light 和派生类型 SpecialLight 和 NormalLight。派生的 classes 都需要 用一些特定的内容初始化静态成员变量“Colors”,而基础 class 应该保持不可实例化。我怎样才能使我的基础class(Light)不可实例化(纯虚拟),同时强制子classes初始化静态变量“Colors”?

class Light
{
    private:
        static const Color Colors[]; // <-- "pure virtual" would be nice here
}

class SpecialLight:Light
{
    private:
        static const Color Colors[] = { Color("yellow"), Color("blue"), Color("magenta") };
}

不幸的是,C++ 中没有纯虚变量,但这正是我在这里需要的。那么我该如何解决这个问题呢?

编辑:派生的 classes 也是单例。

EDIT2:显然这需要更多解释。

Q: 为什么lamp和子class应该是单例? A:这是 lamp 中用于控制特定 LED 亮度的微控制器的代码。但是,此代码用于具有不同 LED 的多个不同 lamp。我的想法是指定一个灯光对象,其中包含控制这些 LED 的所有功能。但是因为 lamps 是不同的(不同的引脚映射等),我需要有不同的灯 classes,每个灯都不同地定义引脚映射、频率等。这个想法是只实例化这些灯 classes 中的一个,它成为单例 class 通过它所有进一步的操作 运行。基础class只是实现了统一接口的功能,永远不会实例化。

Q: 为什么baseclass需要纯虚? A:因为它永远不应该被实例化,因为它缺少关于引脚映射等的信息。从这个意义上说,它不能完全发挥作用。为防止这种情况,如果某些函数不小心尝试实例化它,我想得到一个错误。

Q: 你为什么不把 class 做成摘要,例如通过删除构造函数? A:我需要在基础 class 中实现构造函数,因为所有灯都需要相同的“初始化”功能(初始化 LED 定时器模块),因此我可以'不要声明它是纯虚拟的。

据我了解,您正在尝试表达以下不变量:

Any class derived from Light is known to have a const static member Colors of type Color[].

现在你基本上是正确的 virtual 将东西与对象的运行时类型相关联,如果你能够让这个“东西”不仅包括方法而且包括任意data 然后你就可以将这个 Colors 成员存储在从 Light.

派生的每个 class 的 vtable 中

如果这是您想采用的那种方法,那么它意味着任何时候您想要访问 Colors 您手边都有一个派生 class 的实例。如果那是您的用例,那么您可以只使用虚拟方法,如下所示:

class Light
{
  public:

    virtual std::vector<Color> & getColors() = 0;
};

class NormalLight : public Light
{
  private:

    static std::vector<Color> _colors;

  public:

    std::vector<Color> & getColors() final
    {
      return _colors;
    }
}

但是,这比您从真正的静态成员那里得到的更有限。

如果真的只有一个从 Light 派生的任何 class 的实例,您可以让 Light 拥有一个静态成员,它派生 [=50] =]es 继承,并从它们的构造函数中初始化。然后当调用你拥有的任何一种光的构造函数时,静态成员将被初始化。

class Light
{
  public:

    static Light * instance;

    static std::vector<Color> Colors;

    Light * getInstance()
    {
      assert(instance != nullptr);
      return instance;
    }

    void createInstance(const std::string & type);

};

class NormalLight : public Light
{
  public:

    NormalLight()
    {
      Colors = { RED, GREEN, BLUE };
    }      
};

class SpecialLight : public Light
{
  public:

    SpecialLight()
    {
      Colors = { MAROON, MINT, MIDNIGHT };
    }      
};

// ...

static Light * Light::instance = nullptr;

static std::vector<Color> Light::Colors;

void Light::createInstance(const std::string & type)
{
  assert(instance == nullptr);

  if(type == "Normal")
  {
    Light::instance = new NormalLight();
  }
  else 
  {
    Light::instance = new SpecialLight();
  }
}

这缺少 Light::Colors 常量,但如果我们仅在确定我们是普通灯还是特殊灯后才在运行时确定它的值,这似乎是固有必要的。

最后一种方法是使用模板。这让我们更接近“任何类型的这种形式都有一个叫做某某的成员”的想法。

特别是,您可以只给 NormalLightSpecialLight 它们自己的 const static 成员 Color,然后使用模板的魔力来访问成员那个名字。这是一个例子:

struct ILight
{
  void setColor(Color);
};

template<class T>
class Light : public ILight
{
  private:

    Color myColor;
  
  public:
  
  void setColor(Color newColor)
  { 
    for(Color color : T::Colors)
    {
      if(color == newColor)
      {
        myColor = newColor;
        return;
      }
    }

    throw std::runtime_error("Color not allowed");
  }
  
  Color getColor() { return myColor; }
};


struct NormalLight : public Light<NormalLight>
{
  const static Color Colors[];
};


struct SpecialLight : public Light<NormalLight>
{
  const static Color Colors[];
};

// ...

const Color NormalLight::Colors[] = { RED, GREEN, BLUE };
const Color SpecialLight::Colors[] = { MAROON, MINT, MIDNIGHT };

老实说,我不确定我是否推荐这些方法中的任何一种;事实上,我根本不推荐使用单例。也就是说,我希望这可以解决一些涉及的问题,并帮助您做出权衡取舍的决定。