c++类的常量正确性

Const-correctness for C++ classes

本文关键字:正确性 常量 c++      更新时间:2023-10-16
class SomeClass {
  public:
    void Render() const;
  private:
     mutable Cache m_some_cache;
};

上面的类是const正确的吗?什么时候我可以安全地说"这个操作不会改变实例的内部状态"?

在上面的例子中,SomeClass是渲染屏幕上的东西。它使用缓存(例如OpenGL缓冲区对象)来更快地处理进一步的调用。内部唯一改变的是缓存对象。我在问自己缓存是否已经属于渲染器的内部状态。

这个例子是非常小的,但在我的实际应用程序中,这条路走了很多类,也就是说,很多渲染()调用涉及,他们中的大多数只做缓存。但有些方法也通过资源加载器加载资源——这里的假设仍然正确吗,即方法可能是const的,即使它查询资源管理器来加载资源?

当我们说"内部状态不改变"时,我们指的是一个纯粹的逻辑的东西。改变m_some_cache是否改变对象状态是一个逻辑判断。const正确性是一个逻辑问题。因此,如果您认为从用户的角度来看,改变m_some_cache不会影响对象状态(在逻辑意义上),那么代码是常量正确的。

这样想。这完全有效:

void Type::print_self () const {
    std::cout << *this << std::endl;
}

您没有修改对象本身,因此使用const限定符是完全有效的。这种方法是对std::cout的修改,但这并不算Type::print_self()const

也就是说,在我看来,mutable Cache在术语上是矛盾的,除非您仅将Cache元素用于Render中的本地存储。如果您真的将其用作缓存,那么将此元素限定为mutable似乎有点可疑。将它用作缓存(例如,跨Render调用而不是在Render调用中使用),您对编译器和类的用户都撒了谎。

编辑
根据OP的评论,Render方法确实是print_self()的图形等价物。对象的"真实"状态(为了构造一个最小的工作示例,可能没有显示)可能不会被渲染修改。将Render指定为const方法是正确的做法。如果Cache数据成员存在的原因是作为减速带,避免每次调用Render时构造和销毁它的成本,那么将Cache成员限定为mutable并没有错(这是为了使Render保持const所必需的)。

什么时候我可以安全地说"这个操作不会改变实例的内部状态"?

这个问题是逻辑问题。通常,可变成员不被视为内部状态,而是作为实现工件。因此,类的文档通常应该描述被认为是内部状态的内容,它可以不涉及可变成员。

const只关注对象的内部状态。const -方法可以合法地改变外部状态。这里我们想到了指针的类比:char * const p是一个常量指针,但它可以改变指向的值。所以你的例子与资源管理器似乎也是正确的。

正如@ildjarn所观察到的,常量正确性指的是可观察对象,而不是对象的内部状态;这就是为什么mutable很有用。

然后,如果你实际渲染的东西,那么对象的可观察状态表示屏幕不能合理地const, IMHO,因为它会打破,如果你后来添加一个检查方法来找出什么是在屏幕上/在帧缓冲区。

如果SomeClass不代表屏幕,那么我希望Render接受一个可变引用,比如说,一个Screen对象作为参数。逻辑上,某些必须更改,即使它不是SomeClass实例。

要问自己的问题是,"调用Render函数是否会改变对象的定义状态或未来行为,就用户而言?"

假设你的缓存真的只是一个资源缓存,那么修改缓存可能不会改变功能行为,只是使它更快。因为你的类不能保证它有多慢,所以就调用者而言,这不会改变定义的状态或行为。因此,它是mutable成员的有效候选,可以通过const成员函数进行修改。

Const-correctness是关于从外部看到的对象的状态。

如果对成员函数的所有调用继续返回相同的结果,则对象的状态在逻辑上是相同的。