当操作具有明显的副作用时,Const成员函数

时间:2012-03-21 15:37:41

标签: c++

想象一下,我有一个带有成员函数“show”的类“Window”,这会导致底层窗口在屏幕上显示。

因为我正在调用底层的Windows API来实现这一点,所以我没有以任何方式改变我的“Window”对象的状态,但很明显,由于调用了这个窗口,我的窗口状态发生了很大的明显变化(出现在屏幕上并且能够与用户交互的窗口)

所以我应该将我的函数声明为 -

void Window::show() const;

因为就Window对象而言,此函数不会更改任何状态,因此可以在“const Window”对象上调用。但不知何故,这似乎并不正确,因为在某种意义上明确地调用该函数会改变对象的状态。

5 个答案:

答案 0 :(得分:6)

你可能正在努力解决的问题是C ++没有纯度概念,即函数是否具有副作用。 const成员函数仅承诺不会修改对象本身的状态。

但是,某些对象逻辑上包含的状态多于实例实际包含的状态。当对象的角色与另一个库管理的状态(尤其是GUI库)进行交互时,这种情况经常出现。

因此,虽然show()可以偶然标记为const,但逻辑上应该。如果它真的是const,那么你会期望is_visible()之类的函数在调用show()之前和之后都返回相同的值,显然情况并非如此。< / p>

换句话说,const函数是那些对通过其公共接口可以观察到的对象没有任何改变的函数。这就是背后的原因,例如,标记一些成员函数const,但使用mutable成员进行内部簿记。

答案 1 :(得分:4)

它可能不会改变Window对象中物理保存的任何变量的任何状态,但它确实会改变Window所代表的东西的状态,所以我肯定会认为非const

答案 2 :(得分:4)

这是经典的逻辑const与按位const问题。它是 我在学习C ++(1990年代早期)的时候回想起来,但是从那以后 那么,我认为有一个共识支持逻辑const:如果 对象的逻辑状态发生变化,那么函数就不应该 const(即使编译器允许const),如果是逻辑的 状态不会改变,那么函数应该是const(即使你 需要mutable或抛弃const来做;例如一个缓存的结果 计算)。

Window::show的特定情况下,很难说。是事实 窗口正在显示其“逻辑状态”的一部分。

答案 3 :(得分:2)

如果您希望使用const指针或引用调用该函数,则必须将函数const标记为能够执行此操作。否则完全取决于你。

编辑:给予更多的思考。您的对象可能包含OS窗口的句柄,此句柄可以传递给OS函数。句柄实际上没有const的概念,所以它与C ++的阻抗不匹配,但是你传递给它的函数要么是查询窗口属性(const),要么是以某种方式修改窗口(非const)。即使操作系统不这样做,对象也可以强制执行constness概念。

答案 4 :(得分:0)

通常你有这种情况:

  • 对于每个API窗口,最多只有一个C ++窗口对象。

  • C ++窗口对象不可复制(包括它不可复制)。

对于这种情况,可以在C ++对象接口中使用const,以便让客户端代码提供无状态更改保证。将某些参数声明为const

然而,一旦删除一个或两个约束,它就变成徒劳无益的练习。例如,考虑删除第二个约束,使C ++对象可复制。这似乎是有道理的,将C ++对象看作几乎像shared_ptr个实例(除了破坏)。

但是,任何const shared_ptr都可以复制到非const,因此可以轻松调用任何非const方法。例如,shared_ptr::getconst方法,因此可以在const shared_ptr对象上调用它。但是它的结果是T*,即没有const传播到结果,因为即使它是T const*它也不会真正限制客户端代码:客户端代码只能复制const shared_ptr const 1}}到非get,然后在非const实例上调用const

尽管如此,在上述两个保证的情况下,const(或在您的情况下缺少const可以以可能有用的方式使用在引用的API窗口上进行操作并不意味着这样做是一个好主意。

特别是,表示窗口的对象通常是事件接收器,根据我的经验,事件与const不能很好地混合。

但是,大多数程序员习惯于将纯粹的检查员作为const。对于这种用法,只是提高了代码的可读性,我建议使用const方法。例如,在我看来,返回窗口大小的检查器最好是const而非非{{1}}。