为什么这个const成员函数允许修改成员变量?

时间:2008-11-16 13:21:43

标签: c++ const

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;
}

5 个答案:

答案 0 :(得分:20)

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关键字而无法修改成员)

因为只有一种类型的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成员函数意味着成员函数不会修改对象本身,但并非总是如此,如上例所示。

答案 1 :(得分:4)

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

之间的区别
char*const rep;

const char* rep;

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

现在,请记住rep[i] = ...;*(rep + i) = ...;相同。因此,您更改的不是指针,而是指向的指针。您被允许,因为指针不属于第二种类型。

<强>解决方案

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

答案 2 :(得分:1)

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

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

答案 3 :(得分:1)

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

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

解决这个问题的方法是用std :: string替换char * 然后你只能从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

    }
}

答案 4 :(得分:1)

您无法更改声明为

的内容的值
const char* rep;

const char* const rep;

不幸的是,声明你的成员const变成了代表

char* const rep;

这意味着,您无法更改实际地址,但您可以更改内容,而无法更改值。

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