为什么一个const成员函数可以修改静态数据成员?

90
在下面的C++程序中,在一个const函数中修改一个静态数据成员是可以正常运行的:
class A 
{
  public:   
    static int a; // static data member

    void set() const
    {
        a = 10;
    }
};

但是在一个const函数中修改非静态数据成员将不起作用:

class A 
{
  public:   
    int a; // non-static data member

    void set() const
    {
        a = 10;
    }
};

为什么一个 const 成员函数能够修改一个 static 数据成员?


如果您能告诉我们您使用的平台和编译器,那将非常有帮助。这样我们就可以确定行为是否与您特定的设置相关联,或者该行为确实是正确的,只需要解释一下即可。 - Alex Zywicki
@AlexZywicki 在 Linux 平台上使用 G++ 编译器。 - msc
8
不需要。这是故意的,所有C++编译器都必须支持它。但为什么像这样好的问题不再被点赞了呢? - Bathsheba
18
这个是重复的内容,但由于提供了更好的 MCVE,写得比另一个好,所以我选择这个作为目标。 - Baum mit Augen
更糟糕的是,成员函数 void clobber(A &x) const { x.a = 0; } 是被允许的! - user1084944
5
这里的动机是const表示一个对象的成员函数不能修改该对象。它可以修改同一类的其他对象,或与类相关联而不是特定实例的static数据。(或者是为了违反这个规则而创建的mutable数据成员)。 - Davislor
4个回答

103

这只是规定,而且原因充分。

const 限定符对于成员函数的意思是,您不能修改非 mutablestatic 类成员变量。

为了提供一些合理的解释,const 修饰的成员函数中的 this 指针是一个 const 类型,而且 this 与类的一个实例密切相关。而 static 成员不与类实例相关联。您不需要实例即可修改 static 成员:在您的情况下,您可以通过编写 A::a = 10; 来完成。

所以,在第一个案例中,把 a = 10; 看作 A::a = 10; 的简写;在第二个案例中,把它看作 this->a = 10; 的简写,但这不可编译,因为 this 的类型是 const A*


1
这里只有一个小错误:由于您无法重新分配this指针,因此在const的情况下它将是类型为const A* const - Taylor Hansen
3
this 是指针类型的 prvalue。非类类型的 prvalue 永远不会带有 cv 限定符。 - user743382

22
根据 C++ 标准(9.2.3.2 静态数据成员),1 个静态数据成员并非类的子对象的一部分...
并且(9.2.2.1 this 指针),在非静态成员函数的函数体内,关键字 this 是一个 prvalue 表达式,其值是调用该函数的对象的地址。X 类的成员函数中,this 的类型为 X*。如果成员函数被声明为 const,则 this 的类型为 const X*,...
最后,在非静态成员函数 (9.2.2) 中,如果名称查找 (3.4) 将 id-expression 解析为某个类 C 的非静态非类型成员,并且如果 id-expression 可能被评估,或者 C 是 X 或 X 的基类,则使用 (*this)(9.2.2.1)作为 . 运算符左侧的后缀表达式将 id-expression 转换为类成员访问表达式 (5.2.5)。
因此,在这个类定义中,...
class A 
{
  public:   
    static int a; 

    void set() const
    {
        a = 10;
    }
};

静态数据成员a不是类类型对象的子对象,指针this也不能用于访问静态数据成员。因此,任何成员函数,非静态常量或非常量、或者静态成员函数都可以更改该数据成员,因为它不是常量。

在这个类定义中

class A 
{
  public:   
    int a; 

    void set() const
    {
        a = 10;
    }
};

非静态数据成员a是类类型对象的子对象。为了在成员函数中访问它,可以使用成员访问语法或者隐含的这个语法。您不能使用常量指针this来修改数据成员。在函数set中,指针this确实具有类型const A*,因为该函数声明了限定符const。如果该函数没有此限定符,则可以更改数据成员。


13
问题在于,如果类A的成员函数是const,则this的类型为const X*,从而防止非静态数据成员被修改(例如参见C++标准:链接):

9.3.2 this指针 [class.this]

在非静态(9.3)成员函数主体中,关键字this是一个prvalue表达式,其值是调用该函数的对象的地址。 在类X的成员函数中,this的类型为X*。如果成员函数声明为const,则this的类型为const X*,...

如果a是非静态数据成员,则a=10等同于this->a = 10,如果this的类型为const A*且a未声明为可变的,则不允许。因此,由于void set() const使得this的类型为const A*,这种访问是不被允许的。
相比之下,如果a是静态数据成员,则a=10根本不涉及this;只要static int a本身没有被声明为const,语句a=10是被允许的。

2

const 修饰 成员函数 表示您不能修改 非可变的非静态的 类数据成员


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