【问题标题】:Why does this const member function allow a member variable to be modified?为什么这个 const 成员函数允许修改成员变量?
【发布时间】:2008-11-16 13:21:43
【问题描述】:
class String
{

    private:
        char* rep;

    public:
        String (const char*);
        void toUpper() const;
};


String :: String (const char* s)
{
    rep = new char [strlen(s)+1];
    strcpy (rep, s);
}


void String :: toUpper () const
{
    for (int i = 0; rep [i]; i++)
    rep[i] = toupper(rep[i]);
}


int main ()
{
    const String lower ("lower");
    lower.toUpper();

    cout << lower << endl;
    return 0;
}

【问题讨论】:

    标签: c++ constants


    【解决方案1】:

    一个const成员函数,是一个不改变其成员变量的成员函数。

    成员函数上的 const 并不意味着 const char *。这意味着您无法更改指针所在地址中的数据。

    您的示例不会改变成员变量本身。

    成员函数上的 const 将确保您将所有成员变量视为 const。

    这意味着如果你有:

    int x;
    char c;
    char *p;
    

    那么你将拥有:

    const int x;
    const char c;
    char * const p; //<-- means you cannot change what p points to, but you can change the data p points to
    

    有两种类型的 const 指针。一个 const 成员函数使用我上面列出的那个。


    获取所需错误的方法:

    尝试改变:

    char * rep;
    

    到:

    char rep[1024];
    

    并删除这一行:

    rep = new char [strlen(s)+1];
    

    它会抛出你所期望的错误(由于 const 关键字不能修改成员)

    因为只有 1 种类型的 const 数组。这意味着您无法修改其任何数据。


    现在整个系统实际上被下面的例子破坏了:

    class String
    {
    
        private:
            char rep2[1024];
            char* rep;
    
     ...
    
    
     String :: String (const char* s)
     {
        rep = rep2;
        strcpy (rep, s); 
     }
    

    所以这里要吸取的教训是成员函数上的 const 关键字并不能确保你的对象根本不会改变。

    它只确保每个成员变量都将被视为 const。而对于指针,const char * 和 char * const 之间存在很大差异。

    大多数时候,一个 const 成员函数意味着该成员函数不会修改对象本身,但并非总是如此,如上例所示。

    【讨论】:

    • 非常感谢您的快速响应和非常清晰的描述。 Stack Overflow 真的很震撼
    【解决方案2】:

    原因是你没有更改rep。如果愿意,您会在代码中的某处找到rep = ...;。这就是区别

    char*const rep;
    

    const char* rep;
    

    在你的情况下,如果你执行一个 const 成员函数,第一个就完成了:指针是 const。因此,您将无法重置指针。但是您很可能能够更改指针指向的内容。

    现在,请记住 rep[i] = ...;*(rep + i) = ...; 相同。因此,您更改的不是指针,而是指针指向的内容。可以,因为指针不是第二种情况。

    解决方案

    1. 您正在查看的 const 含义是 physical constness。但是,一个 const 成员函数意味着您的对象是 logical const。如果对某些内容的更改将更改对象的逻辑常量,例如,如果它更改了对象所依赖的某些静态变量,则编译器无法知道您的类现在具有另一个逻辑值。而且它也不知道逻辑值的变化取决于指针指向的内容:编译器不会尝试检查 const 成员函数中的逻辑常量,因为它不知道这些成员变量的含义。这东西被称为const-correctness
    2. 使用不只是引用或指针的对象:const 成员函数将使该对象成为 const,并且不允许您更改其内容。 std::string,正如一些人所建议的那样,或者一个字符数组(请注意,一个数组不允许您更改其内容,而不仅仅是一个指针),将是一个合适的选择。
    3. 2.

    【讨论】:

      【解决方案3】:

      toUpper() 不会更改指针(属于该类)。它只会更改 rep 指向的数据(不属于该类)。

      然而,'const' 是对类的用户的一种保证:如果一个方法被声明为 const,那么使用你类的实例的人可以期望在调用该方法时它不会改变。我的观点是,如果 toUpper() 改变了字符串的状态,不要将它声明为 const,不管 C++ 是否允许。

      【讨论】:

        【解决方案4】:

        const 限定符意味着它不会改变类的任何成员。

        在这种情况下,rep 是该类的唯一成员,我看不到修改该成员的尝试。类外指向引用的任何东西都不会被视为类的一部分。

        解决此问题的方法是将 char* 替换为 std::string。
        那么你将只能从 toUpper() 中调用 std::string 的 const 成员

        例如(使用 std::string)

        class String
        {
            std::string rep;
        
            void toUpper() const
            { 
                for (int i = 0; rep [i]; i++)
                    rep[i] = toupper(rep[i]);
        
                // Can only use const member functions on rep.
                // So here we use 'char const& std::string::operator[](size_t) const'
                // There is a non const version but we are not allowed to use it
                // because this method is const.
        
                // So the return type is 'char const&'
                // This can be used in the call to toupper()
                // But not on the lhs of the assignemnt statement
        
            }
        }
        

        【讨论】:

          【解决方案5】:

          你不能改变声明为的东西的值

          const char* rep;
          

          const char* const rep;
          

          不幸的是,声明你的成员 const 变成了 rep into

          char* const rep;
          

          也就是说,你不能改变实际地址,但你可以改变内容,而你不能改变值。

          为了让 const memebrs 尊重你的缓冲区常量,你需要创建 rep 和字符数组或字符串对象,而不是字符指针。

          【讨论】:

            猜你喜欢
            • 2011-11-15
            • 1970-01-01
            • 1970-01-01
            • 2016-03-03
            • 2017-10-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多