【问题标题】:Initialize "inherited static members" in derived class without instance variable在没有实例变量的派生类中初始化“继承的静态成员”
【发布时间】:2014-05-25 23:04:52
【问题描述】:

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

我发现了一些关于 Stack Overflow 相关问题的建议(hereherehere)。两个常见的建议如下:

  1. 在返回纹理对象引用的基类中创建一个虚拟方法(例如texture),并在每个派生类中覆盖该方法。
  2. 使用curiously recurring template pattern 允许我从基类方法调用派生类的静态成员。

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

我不能使用 (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; }
};

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

【问题讨论】:

  • 这么多话。这么少的代码。
  • 好点。我添加了选项 (1) 的基本示例实现。
  • 一个主要的问题是纹理的加载是否可以找到相关数据并加载它,或者数据是否流式传输,因此必须在流加载的适当点调用以消化以下数据?
  • 您能否将纹理的加载推迟到第一次出现相关粒子的实例?
  • 你不能让纹理自己初始化吗?为什么 load() 不只是 Texture 的构造函数,而 unload() 不是它的析构函数?

标签: c++ inheritance static member derived-class


【解决方案1】:

首先,我建议沿RAII 行重构Texture 类,以便在构造函数中加载纹理,在析构函数中执行卸载。

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

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

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

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

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

另请参阅 C++ 常见问题解答中的 How do I prevent the "static initialization order fiasco"?

顺便说一句,best not to use identifiers with leading underscores

【讨论】:

  • 我对私有成员数据使用前导下划线,这样做是安全的,因为我 (1) 只使用一个下划线,(2) 不使用首字母大写,并且 (3) 不将它们放在文件范围内。我主要使用这个约定,所以访问器/修改器可以使用与私有成员相同的名称。
  • 因为纹理依赖于其他静态对象,我不得不使用你的第二个解决方案。我对删除加载/卸载功能有点犹豫,因为它们让我可以更好地控制资源使用。你会建议使用指针并在需要时删除它们/重新分配它们吗?
猜你喜欢
  • 1970-01-01
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
  • 2012-02-18
  • 1970-01-01
  • 2021-09-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多