Почему эта функция-член const позволяет изменять переменную-член? - PullRequest
10 голосов
/ 16 ноября 2008
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 ]

20 голосов
/ 16 ноября 2008

Постоянная функция-член - это функция-член, которая не изменяет свои переменные-члены.

const для функции-члена не означает const char *. Что означало бы, что вы не можете изменить данные в адресе, который содержит указатель.

Ваш пример не изменяет сами переменные-члены.

Константа в функции-члене гарантирует, что вы рассматриваете все переменные-члены как 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 использует ту, которую я перечислил выше.


Способ получить нужную ошибку:

Попробуйте изменить:

char * rep;

до:

char rep[1024];

И удалить эту строку:

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

Он выдаст ожидаемую ошибку (не может изменить членов из-за ключевого слова const)

Поскольку существует только 1 тип константного массива. А это значит, что вы не можете изменять какие-либо из его данных.


Теперь вся система фактически сломана в следующем примере:

class String
{

    private:
        char rep2[1024];
        char* rep;

 ...


 String :: String (const char* s)
 {
    rep = rep2;
    strcpy (rep, s); 
 }

Таким образом, урок, который следует извлечь из этого, заключается в том, что ключевое слово const для функций-членов не гарантирует, что ваш объект не изменится вообще.

Это только гарантирует, что каждая переменная-член будет рассматриваться как const. А для указателей существует большая разница между const char * и char * const.

В большинстве случаев постоянная функция-член будет означать, что функция-член не будет изменять сам объект, но это не всегда так, как показано в приведенном выше примере.

4 голосов
/ 16 ноября 2008

Причина в том, что вы не меняете rep. Если вы хотите, вы найдете rep = ...; где-то в вашем коде. Это разница между

char*const rep;

и

const char* rep;

В вашем случае первое делается, если вы выполняете функцию-член const: указатель является const. Таким образом, вы не сможете сбросить указатель. Но вы очень хорошо сможете изменить то, на что указывает указатель.

Теперь запомните, rep[i] = ...; совпадает с *(rep + i) = ...;. Таким образом, вы изменяете не указатель, а указатель , указывающий на . Вам разрешено, так как указатель не относится ко второму типу регистра.

Решение

  1. Постоянное значение, на которое вы смотрите: physical constness. Однако постоянная функция-член означает, что ваш объект logical const. Если изменение какого-либо содержимого изменит логическое постоянство вашего объекта, например, если оно изменит некоторую статическую переменную, от которой зависит ваш объект, ваш компилятор не может знать, что ваш класс теперь имеет другое логическое значение. И он также не может знать, что логическое значение изменяется в зависимости от того, на что указывает указатель: Компилятор не пытается проверить логическое постоянство в функции-члене const, поскольку он не может знать, что означают эти переменные-члены. Этот материал называется const-correctness.
  2. Используйте объект, который не является просто ссылкой или указателем: функция-член const сделает этот объект const и запретит вам изменять его содержимое. std::string, как предлагают некоторые, или массив символов (обратите внимание, что массив будет запрещать вам изменять его содержимое, а не только указатель), будет подходящим выбором.
1 голос
/ 16 ноября 2008

Вы не можете изменить значение чего-либо, объявленного как

const char* rep;

или

const char* const rep;

К сожалению, объявление вашего члена const превращается в представителя в

char* const rep;

, что означает, что вы не можете изменить действительный адрес, но вы можете изменить содержимое, тогда как вы не можете изменить значение.

Чтобы соблюдение const memebrs сохраняло ваш буфер const, вам нужно будет создать rep и массив символов или строковый объект вместо символьного указателя.

1 голос
/ 16 ноября 2008

Спецификатор const означает, что он не изменит ни одного члена класса.

В этом случае rep является единственным членом класса, и я не вижу попыток изменить этого члена. Все, что указывает на или со ссылкой вне класса, не считается частью класса.

Решением этой проблемы было бы заменить char * на std :: string.
Тогда вы сможете вызывать только константные члены std :: string изнутри toUpper ()

Например (используйте 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

    }
}
1 голос
/ 16 ноября 2008

toUpper () не меняет указатель (который принадлежит классу). Изменяются только те данные, на которые указывает респ (которые не принадлежат классу).

Однако const - это своего рода гарантия для пользователей вашего класса: если метод объявлен как const, тот, кто использует экземпляр вашего класса, может ожидать, что он не изменится при вызове метода. Моя точка зрения такова: если toUpper () изменяет состояние строки, не объявляет ее const, независимо от того, позволяет ли это C ++ или нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...