如何解决“纯虚拟变量”或:如何强制派生类初始化静态成员变量?
我有一个单例类 Light 和派生类型 SpecialLight 和 NormalLight。两个派生类都需要用一些特定的内容初始化静态成员变量“Colors”,而基类应该保持不可实例化。如何使我的基类 (Light) 不可实例化(纯虚拟),同时强制子类初始化静态变量“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++ 中没有纯虚拟变量,但这正是我在这里需要的。那么我该如何解决这个问题呢?
编辑:派生类也是单例。
EDIT2:显然这需要更多解释。
问:为什么灯和子类应该是单例? A:这是用于控制特定 LED 亮度的灯中的微控制器的代码。但是,此代码用于具有不同 LED 的多个不同灯。我的想法是指定一个光对象,其中包含控制这些 LED 的所有功能。但是因为灯是不同的(不同的引脚映射等),我需要有不同的灯类,每个灯类都以不同的方式定义引脚映射、频率等。这个想法是只实例化这些轻类中的一个,成为所有进一步操作运行的单例类。基类只是实现统一接口的功能,永远不会被实例化。
问:为什么基类需要是纯虚拟的? A:因为它永远不应该被实例化,因为它缺少有关引脚映射等的信息。从这个意义上说,它不能完全发挥作用。为了防止这种情况发生,如果某些函数意外地尝试实例化它,我想得到一个错误。
Q:你为什么不把类抽象化,例如通过删除构造函数? A:我需要在基类中实现构造函数,因为所有灯都需要相同的“初始化”功能(初始化 LED 计时器模块),因此我不能将其声明为纯虚拟的。
解决方法
据我所知,您试图表达以下不变量:
已知从 Light
派生的任何类都具有 const static
类型的 Colors
成员 Color[]
。
现在您基本上正确,virtual 将东西与对象的运行时类型相关联,如果您能够让这些“东西”不仅包括方法,还包括任意数据,那么您就可以能够将这个 Colors
成员存储在从 Light
派生的每个类的 vtable 中。
如果这是您想要采用的那种方法,那么这意味着在您想要访问 Colors
的任何时候,您手头都有一个派生类的实例。如果这是您的用例,那么您可以使用虚拟方法,如下所示:
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
派生的类真的只有一个实例,您可以改为让 Light
拥有一个静态成员,它的派生类继承了该成员,并从它们的派生类初始化构造器。然后当你拥有的任何类型的光的构造函数被调用时,静态成员将被初始化。
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
是常量,但如果我们只在运行时确定我们是普通灯还是特殊灯后才确定它的值,这似乎本质上是必要的。
最后一种方法是使用模板。这让我们更接近于“这种形式的任何类型都有一个名为某某的成员”的想法。
特别是,您可以只给 NormalLight
和 SpecialLight
自己的 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,BLUE };
const Color SpecialLight::Colors[] = { MAROON,MIDNIGHT };
老实说,我不确定我推荐这些方法中的任何一种;事实上,我根本不推荐使用单例。话虽如此,我希望这可以解决一些涉及的问题,并帮助您做出自己的权衡决定。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。