是否可以在C ++类中声明虚拟静态常量值?

时间:2012-06-06 13:50:26

标签: c++ inheritance virtual constants static-members

我想要一个具有常量字段的基类(比如与编译时间后无法修改的类关联的唯一ID)。到目前为止,static const声明会很好。现在,我想继承这个基类,并确保这个类的子类具有相同的字段,但具有自己的值。我怎么能这样做?

让我们说,我希望有一个名为Base的基类,其中ID字段的int值为0.然后,我想拥有课程ABC,所有这些都是Base的公开儿童,我想确保这些孩子也有ID相应值为1,2和3的字段(通过'确定',我的意思是如果他们没有明确声明ID,就会产生编译错误。)

如果我可以设法构建这个场景,我的期望是要求ID指针的Base*字段,我应该得到不同的值,这取决于指针是否创建为{{1 }},new A()new B()

我的猜测是将new C()声明为ID,这当然没有意义并且会产生编译错误。

但是我能做些什么才能达到上述结果呢? (我唯一可以想象的是将virtual static const声明为返回整数的虚函数,然后将值硬编码到函数体中,但我正在寻找更优雅的东西。)

提前谢谢!

2 个答案:

答案 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::nameShape::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函数将始终返回对完全相同对象的引用。在只读虚拟静态成员上,这没有什么区别(除非构造函数实际上以不同的方式初始化每个实例),但是在读写虚拟静态成员上,更新它们将有所作为。