为什么将函数声明为const有用?

3
为什么将函数声明为const很有用,即使您只能读取变量而无法写入(类变量)?

1
你可以写变量。但你不能写类变量。 - Falmarri
我是想那个的,但是我忘记包含它了。 - Ramilol
6个回答

3
为了避免您“意外”修改类变量,这只是一项安全措施。
(如果您在函数后使用const关键字,而该函数确实修改了类的任何数据成员 - 直接或通过另一个函数调用 - 您将会得到编译错误)。

请注意,这并不是非常安全的措施;您始终可以通过强制转换来规避它。 - dan04
2
@dan04:这是一个误解。只有在非常罕见的情况下,才可以使用 const_cast 修改对象,基本上只有在 const_cast 的用户最初添加了 const 修饰符到原本不是 const 的指针或引用时才合法。 - Ben Voigt

3

如果你将你的类的实例作为const指针或const引用传递给其他对象,则只能调用该类的const方法(如果有的话)。

显然,如果您从不关注类型的const正确性,则可以忽略此问题。

我认为这也可能在某些情况下帮助编译器优化代码,尽管我对此表示怀疑,即使它确实有所帮助,在大多数情况下,允许这种微小的改进来决定你如何编写代码将是一种过早优化的情况。


1
关于优化:编译器可以基于常量正确性进行激进的优化,尤其是当方法体对翻译不可见时。这对编译器可能像restrict关键字一样有用(因为它不必加载和存储可能更改的每个地址,所以可以生成10倍以上的代码)。换句话说,如果您启用了优化并遵循常量正确性,则编译器可能会将函数视为const,并假设它基于对象数据的值没有更改,因此不需要重新读取。 - justin
我觉得很遗憾,C++程序员总是过于关注性能,而软件挑战大多与正确性有关。 - David Heffernan

2

一个原因是const是一种病毒。这意味着如果代码的一部分是const-correct,那么其他代码就无法与该部分进行交互。

如果忽略const-correctness,你的类很难与其他库(以标准库开头)配合工作。

例如:

#include <vector>
#include <algorithm>

struct X
{
    int n;
    bool operator< (X b)
    { 
        return n < b.n;
    }
};

int main()
{
    std::vector<X> vec;
    std::sort(vec.begin(), vec.end());
}

使用codepad.org

In function 'const _Tp& std::__median(const _Tp&, const _Tp&, const _Tp&) [with _Tp = X]':
/usr/local/lib/gcc/i686-pc-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_algo.h:2642:   instantiated from 'void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size) [with _RandomAccessIterator = __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<X*, __gnu_norm::vector<X, std::allocator<X> > >, __gnu_debug_def::vector<X, std::allocator<X> > >, _Size = int]'
/usr/local/lib/gcc/i686-pc-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_algo.h:2713:   instantiated from 'void std::sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<X*, __gnu_norm::vector<X, std::allocator<X> > >, __gnu_debug_def::vector<X, std::allocator<X> > >]'
t.cpp:17:   instantiated from here
Line 90: error: no match for 'operator<' in '__a < __b'

一个符合stdlib的比较运算符必须保证参数不被修改。如果在比较过程中对象实际上发生了变化,那么对它们进行排序的尝试将是徒劳的。

另一个例子:传递大对象的常规方式是通过const引用传递参数。但现在你必须通过可修改的引用来传递参数。现在您无法将任何类型的临时对象传递给函数。


0
如果您有一个const对象,它只允许const成员函数对其进行操作。

0

你在代码中表达调用方和被调用方之间的契约,而不是文档,越多,编译器就能更好地帮助你遵守这个契约(从两端)。隐式 this 指针的 const-ness 以及所有其他参数引用的 const-ness 都是其中重要的部分。(当然,按值传递的顶层 constness 不是契约的一部分)


0
一个好处是你可以让编译器强制执行状态的修改位置。例如,如果你创建了一个包含私有数据和所有方法(除了构造函数)都是const的类,那么你就拥有了一个不可变的数据类型。这样做的好处不是性能,而是语义上的优势。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接