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

5 回答 5

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

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

于 2008-11-16T13:33:49.900 回答
4

原因是你没有改变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.
于 2008-11-16T13:42:51.440 回答
1

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

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

于 2008-11-16T13:26:11.787 回答
1

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

    }
}
于 2008-11-16T13:31:10.980 回答
1

您不能更改声明为的东西的值

const char* rep;

或者

const char* const rep;

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

char* const rep;

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

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

于 2008-11-16T15:39:23.843 回答