c++只允许成员变量被设置一次

C++ Only Allow a member variable to be set once

本文关键字:设置 一次 变量 成员 许成员 c++      更新时间:2023-10-16

我接触c++已经有一段时间了,但我想确保我在做的时候坚持最佳实践,包括const正确。

我目前正在为游戏框架构建一个代码库,我有以下类(简单总结):

class Screen
{
public:
Clear();
}

class Game
{
private:
Screen* screen;
protected:
const Screen* GetScreen() const;
}

为简洁起见,我省略了其余部分,但可以这么说,Game类负责创建Screen类实例。创建后,只能通过GetScreen()方法访问。

我的想法是,当创建一个新游戏时,我将继承这个基类,所以作为一个例子,在渲染循环期间,我想要清除屏幕以重新绘制下一帧。现在,在我上面的定义中,在使用GetScreen()方法时不允许调用Clear()方法,因为它不是const。clear方法实际上会改变类的内部工作(通过清除先前显示的图像这一事实),因此我将const声明排除在定义之外。如果我使它成为const,那么我将不得不拥有Screen类的一些内部工作方式作为mutable,但从我所读到的,这将不是非常const正确的。

所以,我的问题有两个部分。我是否应该将void Clear()更改为void Clear() const,并将内部工作部分更改为mutable?

或者是另一种选择,允许我使Game类的screen成员只能由Game类设置一次,以便我可以在程序运行时的其余时间访问非const成员函数。

谢谢你的帮助。

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

class Game
{
Screen *screen;
protected:
const Screen* GetScreen() const;
Screen* GetScreen();
};

它们都不允许派生类修改screen指针本身,所以它不能"重置"它指向Game不希望它指向的地方。

'const'的概念用于表明对象的内部状态不应该被修改。

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

将非静态成员标记为const的能力允许您强制const引用的对象的持有者不能修改内部状态。例如,在下面的场景中,Object有一个访问器str,它返回对内部状态的引用,该内部状态同时具有非const和const重载:

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-正确性就没有任何用处,如果您简单地将Game的所有成员都设置为非const,则可以避免您自己的头痛。

我强烈建议你更新一下c++中指针的知识。现在几乎没有理由使用原始指针了。

直接回答这个问题,Game.GetScreen()函数想要'const'的方式不同,这取决于你希望它如何表现。

如果你返回一个const Screen*,你返回一个指向Screen对象的指针,这个对象是常量(不能被修改)。通常情况下,你会希望将这样一个函数设置为const,允许它在一个自身为const的游戏对象上被调用。如果某人拥有非const Game对象,你可以允许他们获得Screen对象,然后通过返回Screen*进行修改。

这就是说,我回到我开始的观点,你不应该使用原始指针。

最初,您应该只按值存储Screen对象,并返回引用,为您提供如下行:

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

这看起来像是复制Screen对象来返回它,但是返回类型确保您返回的是对相同Screen对象的引用,而不是副本。

如果你真的需要动态创建Screen对象(即,你不能在创建Game对象之前或在创建Game对象时创建它),那么你应该使用std::unique_ptr<Screen>。这可以像原始指针一样使用,但会为您处理很多功能。但是,您希望共享对这个Screen指针的访问,因此您可能希望使用std::shared_ptr<Screen>并从get函数返回std::weak_ptr<Screen>。weak_ptr可以用来允许其他人访问,但是你的初始Game对象仍然拥有Screen对象。

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