关于C++:在没有实例变量的派生类中初始化“继承的静态成员”

Initialize “inherited static members” in derived class without instance variable

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

我发现了一些关于相关堆栈溢出问题的建议(这里、这里和这里)。两种常见的建议如下:

  • 在返回纹理对象引用的基类中创建一个虚拟方法(例如,texture),并在每个派生类中重写该方法。
  • 使用奇怪的循环模板模式,允许我从基类方法调用派生类的静态成员。
  • 这两个都是很好的解决方案,但对于我来说都不太理想。

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

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    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行重新构造纹理类,以便在构造函数中执行纹理加载,并在析构函数中执行卸载。

    然后您就可以静态初始化纹理了。在派生粒子的实现(.cpp)文件中,您将放置:

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

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

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

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

    另请参见如何防止"静态初始化顺序失败"?在C++ FAQ中。

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