我想要一个具有常量字段的基类(比如与编译时间后无法修改的类关联的唯一ID)。到目前为止,static const
声明会很好。现在,我想继承这个基类,并确保这个类的子类具有相同的字段,但具有自己的值。我怎么能这样做?
让我们说,我希望有一个名为Base
的基类,其中ID
字段的int
值为0.然后,我想拥有课程A
,B
和C
,所有这些都是Base
的公开儿童,我想确保这些孩子也有ID
相应值为1,2和3的字段(通过'确定',我的意思是如果他们没有明确声明ID,就会产生编译错误。)
如果我可以设法构建这个场景,我的期望是要求ID
指针的Base*
字段,我应该得到不同的值,这取决于指针是否创建为{{1 }},new A()
或new B()
。
我的猜测是将new C()
声明为ID
,这当然没有意义并且会产生编译错误。
但是我能做些什么才能达到上述结果呢? (我唯一可以想象的是将virtual static const
声明为返回整数的虚函数,然后将值硬编码到函数体中,但我正在寻找更优雅的东西。)
提前谢谢!
答案 0 :(得分:15)
static
方法不能为virtual
,且数据成员不能为virtual
。
但您可以隐藏派生类中的static
字段,并使用virtual
方法返回它们。
class A
{
public:
static const int ID = 0;
virtual int getID() { return A::ID; }
};
class B : A
{
public:
static const int ID = 1;
virtual int getID() { return B::ID; }
};
替代:
class A
{
public:
A(int id = 0) : ID(id) {}
const int ID;
getID() { return ID; }
};
class B : public A
{
public:
B() : A(1) {}
};
答案 1 :(得分:0)
在C ++中具有虚拟静态成员确实非常有用。可以轻松地将它们添加到语言中,不需要新的关键字。以下代码示例为图形库命名形状类型:
class Shape {
public:
static constinit virtual std::string name = delete;
static constexpr virtual bool closed = delete;
...
};
class Circle : public Shape {
public:
static constinit std::string name override { "circle" };
static constexpr bool close override { true };
...
};
class Line : public Shape {
public:
static constinit std::string name override { "line" };
static constexpr bool close override { false };
...
};
这将Shape
声明为抽象基类,因为Shape::name
和Shape::closed
的构造已通过= delete
显式跳过。
虚拟静态成员的空间可以在同一VTable中分配,该VTable已经用于虚拟函数调用。如果所有虚拟静态成员都是constinit
(C ++ 20新增)或constexpr
,则VTable可以写入只读内存,大多数编译器当前也将其写入。如果没有,则必须将VTable放置在读写存储器中。
通常,虚拟静态成员不必是const
,它们也可以是可读写的。
虚拟静态成员都可以使用类名作为前缀(它们的行为类似于普通静态成员)或通过对象(对象的VTable指针将用于访问正确的VTable)进行访问。
只要它们不在标准中,就可以使用虚拟函数对它们进行仿真,这些虚拟函数返回对局部静态变量的引用:
virtual const std::string& get_name() const {
static const std::string name { "circle" };
return name;
}
在派生类未覆盖静态成员(分别为虚拟getter函数)的情况下,真实虚拟静态成员与模拟的虚拟静态成员之间的语义有些不同:父代之间的真实虚拟静态成员子类实际上会引用该对象的不同实例,而构造函数将调用父对象和每个子对象,而不会覆盖虚拟静态成员。但是模拟的getter函数将始终返回对完全相同对象的引用。在只读虚拟静态成员上,这没有什么区别(除非构造函数实际上以不同的方式初始化每个实例),但是在读写虚拟静态成员上,更新它们将有所作为。