C++成员修饰const和mutable

const:常量,不变的 mutable:易变的从意思上理解,可见 constmutable 是一对反义词,它们都是 C++的关键字。const 成员函数不能修改调用它的对象。类的成员函数可以被声明为 const,这将使得函数的隐式参数 this 将被作为 const 类型的指针。这也就意味着一个 const 成员函数不能修改调用它的对象。而且,const 对象不能调用非 const 成员函数。然而,const 对象和非 const 对象都可以调用 const 成员函数。要将一个成员函数声明为 const,可以使用下面的形式:

class X
{
    int some_var;
public:
    int f1() const;    //const成员函数
};
```cpp
可以看到,关键字 `const` 被放在函数声明之后。将一个成员函数声明为 `const` 的目的是防止函数修改调用它的对象。例如下面的代码:
```text
#include 
using namespace std;

//说明const函数的用法,这个程序不能通过编译
class Demo
{
    int i;
public:
    int geti() const
    {
        return i;        //正确
    }
    void seti(int x) const
    {
        i = x;          //错误!
    }
};

int main()
{
    Demo ob;
    ob.seti(1900);
    cout << ob.geti() << endl;
    return 0;
}
```cpp
因为函数 `seti()` 被声明为 `const` 成员函数,这意味着在函数中不能修改调用函数的对象。但是由于 `seti()` 试图修改成员变量 i,所以程序会产生错误,而在函数 `geti()` 中并不修改成员变量 i,所以这个函数是正确的。有时你可能想在 `const` 函数中修改类的某些成员,但又不想让函数修改类的其它成员,那么可以通过关键字 `mutable` 来实现这种功能。`mutable` 将覆盖 `const` 属性。也就是说,在 `const` 成员函数中可以修改 `mutable` 成员,例如:
```text
#include 
using namespace std;

//可以通过编译,运行正确
class Demo
{
    mutable int i;
public:
    int geti() const
    {
        return i;                //正确
    }

   void seti(int x) const
   {
        i = x;                  //正确
   }
};

int main()
{
    Demo ob;
    ob.seti(1900);
    cout << ob.geti() << endl;
    return 0;
}
```cpp

在上面的程序中,类的成员变量 i 被定义为 `mutable`,所以在函数 `seti()` 中可以修改它的值。
## const 成员变量的初始化
在构造函数中对成员变量进行初始化是很普遍的初始化方法,然而,这种方法并不是适用于所有情况,例如:如果在类的定义中使用了 `const` 来声明成员变量,那么在类的构造函数中将不能对这些成员变量赋初始值,因为 `const` 变量必须在构造函数调用之前被初始化,在使用引用类型的成员以及没有默认构造函数的成员时存在着同样的问题,因为这些成员必须首先被初始化。为了解决这个问题,在 C++中定义了一种成员初始化语法,可以在创建对象时为类的成员指定初始值。成员初始化语法类似于调用基类构造函数的语法,它的通用形式如下所示:
```text
constructor(arg_list)
     : member(initializer)
     , member(initializer)
     , member(initializer)
{
    //构造函数体
}
```cpp
在构造函数的后面指定你想要初始化的成员,同时用冒号将构造函数的名字和参数列表分开。也可以将基类构造函数的调用和成员的初始化放在同一参数列表中。

   转载规则


《C++成员修饰const和mutable》 吴杭沉 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
内存分配malloc 内存分配malloc
下面的代码片段输出是什么?为什么? char *ptr; if((ptr = (char *)malloc(0)) == NULL) puts("Got a null pointer"); else puts("Got a
2018-08-10
下一篇 
使用++i还是i++ 使用++i还是i++
我们经常使用 for 循环来遍历东西,循环变量可以前自增也可以后自增,发现对遍历结果没啥影响,但是该如何选择呢?我们应该尽量使用前自增运算符而不是后自增运算符,即用 ++Iter 代替 Iter++。前缀和后缀形式之间的区别是众所周知的。我
2018-08-04
  目录