我有一个 getter,它返回一个临时对象,即内部对象的副本。
类似于以下内容:
class Foo
{
public:
QString name() {return m_name;}
void setName(const QString &name);
private:
QString m_name;
}
Run Code Online (Sandbox Code Playgroud)
setter/getter 的预期用途是获取数据name()并通过设置数据setName()。
但有时,错误地,我倾向于执行以下操作:
name().clear();
Run Code Online (Sandbox Code Playgroud)
或者直接对 getter 的返回进行一些其他修改。
请注意,上述行(以及类似的修改)的目的是清除 Foo,而不是临时变量,因此上面的行可以正确编译,但行为不正确,因为它将清除临时成员,而不是 Foo 的成员。
如何禁用这种不正确的用法,并在编译时出错?
请注意,我可能同时拥有第三方类型(我无法修改,例如 std::string 或上面的 QString)和自定义类型(我可以为此修改)。
编辑:
需要注意的是超载const,&,&&或类似的是没有用的,因为我可能是这样的:
Foo foo;
foo.name().clear();
Run Code Online (Sandbox Code Playgroud)
无论您选择什么重载,它都会编译得很好。
此外,对内部成员的所有修改都必须经过setName(),因为它会应用一些逻辑并通知其他一些对象,因此必须避免任何不是通过接口进行的修改。这可以防止 getter 通过非常量引用返回以允许上述用法或将私有成员设为公共成员。
也许试试
const QString name() const {return m_name;}
Run Code Online (Sandbox Code Playgroud)
这将阻止直接使用:
foo.name().clear();
Run Code Online (Sandbox Code Playgroud)
另请注意,如评论中下划线所示,由于返回的 QString 是副本,foo.name().clear();不会修改foo.m_name. 但是,是的,快速阅读代码可能会令人困惑。
编辑额外读取:when-use-const-3-return-types
一个更复杂的解决方案,灵感来自关于 getter-and-setter的想法,将是
class Foo {
public:
...
const QString& name() const & {return m_name;}
QString name() && {return std::move(m_name);}
private:
QString m_name;
};
Run Code Online (Sandbox Code Playgroud)
另请注意,该博客文章鼓励使用这样的简单解决方案:
class Foo {
...
public
QString name;
};
Run Code Online (Sandbox Code Playgroud)
当您不必在“setter”方法中强制执行某些不变量或执行某些其他操作时,这是一种经济且可行的解决方案。