仅允许C++成员变量设置一次

3

我已经有一段时间没有接触 C++ 了,但是当我重新开始写代码时,我想确保自己能够遵循最佳实践,包括保持 const 正确。

我目前正在构建一个游戏框架的代码库,以下是我整理的几个类(出于简化的考虑):

class Screen
{
public:
    Clear();
}

并且

class Game
{
private:
    Screen* screen;

protected:
    const Screen* GetScreen() const;
}

我为了简洁起见省略了其余内容,但要说的是,Game类负责创建Screen类的实例。一旦创建完成,应仅通过GetScreen()方法访问。
这样做的想法是,当创建新游戏时,我会从这个基类继承,因此,在循环渲染期间,例如我希望清除屏幕以重新绘制下一帧。在上述定义中,Clear()方法不允许在使用GetScreen()方法时调用,因为它不是常量。清除方法实际上会更改类的内部工作方式(因为先前显示的图像被清除),因此我将定义中的const声明留空。如果我将其设置为const,那么我就必须将Screen类的某些内部工作部分设置为mutable,但根据我的阅读,这并不是非常恰当。
所以,我有两个问题。我是否应将void Clear()更改为void Clear() const并使内部工作部分可变?
还是说有另一种选择可以让Game类的成员screen只能由Game类设置一次,以便我可以在程序的其余运行时间访问非常量成员函数?
感谢任何帮助。

4
看起来你希望 screen 成为 Screen& 或者 Screen* const,而不是 const Screen* - Casey
3个回答

1

由于Game::screen是私有的,派生类无法访问它。虽然GetScreen()的调用者可以访问Screen对象,但他不能修改Game存储的screen指向的内容。因此,您可以提供以下两个重载函数:

class Game
{
  Screen *screen;

protected:
  const Screen* GetScreen() const;
  Screen* GetScreen();
};

它们都不允许派生类直接修改screen指针本身,因此不能将其“重置”为Game不希望指向的地方。


0

'Const'的概念很有用,可以表示对象的内部状态不应该被修改。

void f(const Object& o)
{
    // 'o' cannot be modified
}

将非静态成员标记为const的能力使您能够强制执行对对象的const引用的持有者不能修改内部状态。例如,在以下场景中,Object具有一个访问器str,它返回对内部状态的引用,该状态具有非常量和常量重载:
struct Object
{
    // non-const: caller can modify internal state
    std::string& str();

    // const: caller cannot modify internal state
    const std::string& str() const;
};

非 const 重载允许修改内部状态,但只能在对对象的非 const 引用上调用;const 重载不允许修改内部状态,并且可以与 const 和非 const 对象引用一起使用。

在您提出的情况中,Game 似乎是一个包含程序中所有内容的单体 单例 对象。如果是这样,那么传递到 Game 的引用 - 或者从它派生的对象 - 是否有用似乎值得怀疑,这使得 const Game&Game& 之间的区别有点无关紧要。如果是这种情况,const-correctness 没有任何用处,如果您将 Game 的所有成员都设置为非 const,则可以避免头痛。


我认为我会按照你的建议,将Game的成员变量设置为非const。虽然它不是真正的单例模式,但它实际上是入口点。例如,主函数将创建一个新的Game对象并调用其唯一的公共方法Run(),其余部分将从那里处理。Game的派生类将负责任何其他所需的对象,并且不应需要访问游戏。当我不需要事物是const时,我可能只是过度思考了。 - Adam Goss

-2

我强烈建议您更新C++指针的知识。现在几乎没有理由使用原始指针。

直接回答问题,Game.GetScreen()函数会根据您希望它如何运作而不同地“const”。

如果您返回一个const Screen*,则返回一个指向常量(无法修改)Screen对象的指针。通常您希望使这样的函数成为const,以便可以在const Game对象上调用它。如果有人有一个非const Game对象,则可以通过返回Screen*来允许他们获取屏幕对象,然后进行修改。

话虽如此,我还是要回到我的开头观点,您不应该使用原始指针。

最初,您应该按值存储屏幕对象,并返回引用,从而得到类似于以下内容的类:

Class Game{
    Screen screen;
public:
    const Screen& GetScreen() const{ return screen; }
    Screen& GetScreen(){ return screen; }
};

这看起来像是你在复制Screen对象并返回它,但返回类型确保你返回的是对同一屏幕对象的引用,而不是副本。

如果你真的需要动态创建Screen对象(即,在创建Game对象之前或同时无法创建它),那么你应该使用std::unique_ptr<Screen>。这可以非常类似于原始指针使用,但会为你处理很多功能。然而,你希望共享访问这个Screen指针,所以你可能想使用std::shared_ptr<Screen>并从get函数返回std::weak_ptr<Screen>。weak_ptr可用于允许其他人访问,但你最初的Game对象仍然“拥有”Screen对象。

长话短说,按值存储Screen对象,并返回对它的引用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接