在没有实例变量的情况下初始化派生类中的"inherited static members"

Initialize "inherited static members" in derived class without instance variable

本文关键字:inherited members static 派生 初始化 实例 变量 情况下      更新时间:2023-10-16

我正在为一款用c++编写的游戏设计一个抽象粒子类。每个派生的粒子类都有一个不同的纹理,用于该类的所有实例。当然,我想使用静态成员来存储纹理。然而,纹理的加载、卸载和绘制对于每个粒子都是相同的。因此,我也想在基本粒子类中编写这些函数,而不是为每个派生类重写它们。问题是c++中没有办法创建静态的虚成员。

我找到了一些有关Stack Overflow问题的建议(在这里,在这里,在这里)。两个常见的建议如下:

  1. 在基类中创建一个虚拟方法(例如,texture),它返回对纹理对象的引用,并在每个派生类中重写该方法。
  2. 使用奇怪的循环模板模式,允许我从基类方法中调用派生类的静态成员。

这两个都是很好的解决方案,但对于我的目的来说似乎都不太理想。

我不能使用(2),因为派生类不共享公共基类,所以我不能使用多态性来存储指向不同派生粒子的混合指针。因此,我需要一个单独的容器来存放我想要添加到场景中的每种类型的粒子,这不是很容易维护的。

选项(1)适用于draw功能。但是正如问题标题所暗示的那样,我没有一个实例变量来从loadunload方法中调用texture。这些方法应该是静态的。这两个函数都是一行函数,因此为每个子类编写这两个函数并不困难。然而,这样做有点不安全,因为没有任何东西强迫我实现这些函数,即使每个子类都需要它们。此外,我只是想知道是否有一个更好的解决方案,如果我或其他人遇到这个问题与更复杂的方法类层次结构(需要更多的代码复制)。

可能没有更好的解决方案,但我想我应该问一下这里是否有人能想到一个,或者也许解释一下我如何改进我的设计来避免这个问题。

编辑:这是选项(1)的一个示例实现,因为Kerrek SB指出我的问题缺少代码。

class BaseParticle
{
public:
    // Cannot declare virtual load and unload methods because they're static.
    void draw()
    {
        texture().draw();
    }
protected:
    virtual Texture& texture() = 0;
};
class DerivedParticle : public BaseParticle
{
public:
    static void load() // Need to reimplement for every other derived class.
    {
        _texture.load();
    }
    static void unload() // Need to reimplement for every other derived class.
    {
        _texture.unload();
    }
private:
    static Texture _texture; // Need to create for every derived class.
    virtual Texture& texture() { return _texture; }
};

希望我的例子和注释能使问题更清楚一些。我基本上是在尝试避免每个静态方法所强加的样板代码。

首先,我建议沿着RAII线重构Texture类,这样纹理的加载在构造函数中执行,卸载在析构函数中执行。

然后你将能够静态初始化纹理。在DerivedParticle的实现(.cpp)文件中,您将放入:

static Texture DerivedParticle::texture_("derivedtexturefilename");

之后,派生的粒子在静态初始化期间获得正确的纹理加载,并且不需要首先调用load/unload方法-这甚至更好,因为您避免了使用未初始化纹理或忘记卸载纹理的可能性。

这假设纹理文件名在编译时是已知的,并且纹理与其他静态对象的初始化顺序并不重要。如果没有,那么您可以通过将静态实例包装在函数中来推迟初始化,并将对它的所有使用替换为对该函数的调用:

Texture& DerivedParticle::texture_() {
    static Texture* texture = new Texture(fileName);
    return *texture;
}

参见如何防止"静态初始化顺序惨败"?

顺便说一句,最好不要使用带下划线的标识符