什么时候成员函数应该有const限定符,什么时候不应该?

时间:2010-03-17 14:21:55

标签: c++ const-correctness

大约六年前,一位名叫Harri Porten的软件工程师写了this article,问了一个问题,“成员函数应该何时具有const限定符,何时不应该?”我发现这是我能找到的关于这个问题的最好的写作,我最近一直在努力解决这个问题,而且我认为在大多数关于const正确性的讨论中,这个问题都没有得到很好的解释。由于当时不存在像SO这样强大的软件信息共享网站,我想在这里重新提出这个问题。

6 个答案:

答案 0 :(得分:5)

这篇文章似乎涵盖了很多基础,但作者仍然有一个关于返回指针的函数的const和非const重载的问题。文章的最后一行是:

许多人可能会回答“这取决于”。但我想问“这取决于什么?”

绝对准确,它取决于A对象指针对象的状态是否在逻辑上属于this对象的状态。

对于它的示例,vector<int>::operator[]返回对int的引用。 int referand是向量的“一部分”,尽管它实际上不是数据成员。所以const-overload惯用法适用:更改元素并且你已经改变了向量。

如果不是,请考虑shared_ptr。这具有成员函数T * operator->() const;,因为将const 智能指针赋予非const 对象具有逻辑意义。 referand不是智能指针的一部分:修改它不会改变智能指针。因此,您是否可以“重置”智能指针以引用不同对象的问题与参考是否为常量无关。

我认为我不能提供任何完整的指导方针来让您决定指针对象是否在逻辑上是对象的一部分。但是,如果修改指针对象会更改this的任何成员函数的返回值或其他行为,特别是如果指针对象参与operator==,那么它很可能在逻辑上属于this的一部分对象

我会错误地假设它部分(并提供重载)。然后,如果出现编译器抱怨我正在尝试修改从const对象返回的A对象的情况,我会考虑是否真的应该这样做,如果是这样,那么更改设计以便只有< em>指向A的指针在概念上是对象状态的一部分,而不是A本身。这当然要求确保修改A不会做任何破坏this const对象的预期行为的事情。

如果你要发布接口,你可能需要事先弄明白这一点,但实际上从const重载返回到const-function-returning-non-const-pointer不太可能破坏客户端代码。无论如何,当你发布一个界面时,你希望它有点使用它,并且可能会感觉到你的对象的状态真的包含了什么。

不过,我也试图在不提供指针/引用访问器方面犯错,尤其是可修改的访问器。这实际上是一个单独的问题(Demeter法则和所有这些),但你可以取代的次数越多:

A *getA();
const A *getA() const;

使用:

A getA() const; // or const A &getA() const; to avoid a copy
void setA(const A &a);

你担心这个问题的次数越少。当然后者有其自身的局限性。

答案 1 :(得分:4)

我在研究这个问题时发现的一个有趣的经验法则来自here

LogicalConst的一个好的经验法则如下:如果一个操作保留了LogicalConstness,那么如果将旧状态和新状态与EqualityOperator进行比较,结果应为true。换句话说,EqualityOperator应该反映对象的逻辑状态。

答案 2 :(得分:1)

我个人使用一个非常简单的经验法则:

如果在调用给定方法时对象的可观察状态没有改变,则此方法应该是 const

一般来说,它类似于SCFrench关于平等比较的规则,除了我的大部分课程都无法比较。

我想进一步推动辩论:

当需要参数时,如果参数保持不变(对于外部观察者),函数应该通过 const 句柄(或复制)来获取 >

稍微更一般,因为毕竟一个类的方法只不过是一个独立的函数接受一个类的实例作为第一个参数:

class Foo { void bar() const; };

相当于:

class Foo { friend void bar(const Foo& self); }; // ALA Python

答案 3 :(得分:0)

当它不修改对象时。

它只会使this具有const myclass*类型。这保证了对象不会改变的调用函数。允许对编译器进行一些优化,并且程序员更容易知道他是否可以在没有副作用的情况下调用它(至少对对象产生影响)。

答案 4 :(得分:0)

一般规则:

  

成员函数应该是const,如果它在标记为const 时编译,并且如果const在传递指针时仍然可以编译。< / p>

<强> 例外:

  • 逻辑const操作;更改内部状态的方法,但使用类的接口无法检测到更改。例如,Splay树查询。
  • const / non-const实现仅因返回类型而不同的方法(与返回iterator / const_iterator的方法相同)。通过const_cast调用const版本中的非const版本是可以接受的,以避免重复。
  • 与不正确的第三方C ++接口的方法,或者使用不支持const的语言编写的代码

答案 5 :(得分:0)

以下是一些好文章:
Herb Sutter's GotW #6
Herb Sutter & const for optimizations
More advice on const correctness
From Wikipedia

当方法不改变类的数据成员或其共同意图不是修改数据成员时,我使用const方法限定符。一个示例涉及用于 getter 方法的RAII,该方法可能必须初始化数据成员(例如从数据库检索)。在此示例中,该方法仅在初始化期间修改数据成员一次;所有其他时间它都是不变的。

我允许编译器在编译期间捕获 const 错误,而不是在运行时(或用户)捕获它们。