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;
}
Почему эта функция-член-член допускает изменение переменной-члена?
Ответ 1
Функция-член const - это функция-член, которая не мутирует ее переменные-члены.
const в функции-члене не подразумевает const char *. Это означает, что вы не можете изменить данные в адресе, который имеет указатель.
Ваш пример не мутирует сами переменные-члены.
A 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 member использует ту, которую я перечислял выше.
Способ получения нужной вам ошибки:
Попробуйте изменить:
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. А для указателей существует большой diff между const char * и char * const.
В большинстве случаев функция-член const будет означать, что функция-член не будет изменять сам объект, но это не всегда так, как показывает приведенный выше пример.
Ответ 2
Причина в том, что вы не меняете rep
. Если бы вы были, вы бы нашли rep = ...;
где-то в своем коде. В этом разница между
char*const rep;
и
const char* rep;
В вашем случае первый выполняется, если вы выполняете функцию-член const: указатель const. Таким образом, вы не сможете reset указатель. Но вы очень хорошо сможете изменить то, на что указывает указатель.
Теперь помните, что rep[i] = ...;
совпадает с *(rep + i) = ...;
. Таким образом, вы меняете не указатель, а указатель. Вам разрешено, поскольку указатель не относится ко второму типу case.
Решение
- Значение const, которое вы смотрите, это
physical constness
. Однако функция-член const означает, что ваш объектlogical const
. Если изменение какого-либо контента изменит логическую константу вашего объекта, например, если он изменяет какую-либо статическую переменную, от которой зависит ваш объект, ваш компилятор не может знать, что ваш класс теперь имеет другое логическое значение. И ни то, ни другое не может знать, что логическое значение изменяется в зависимости от того, на что указывает указатель: компилятор не пытается проверить логическую константу в функции-член const, поскольку он не может знать, что означают эти переменные-члены. Этот материал называетсяconst-correctness
. - Используйте объект, который не является только ссылкой или указателем: функция-член const будет создавать этот объект const и запретит вам изменять его содержимое.
std::string
, как было предложено некоторыми, или массив символов (обратите внимание, что массив будет запрещать вам изменять его содержимое, а не только указатель), был бы подходящим выбором. 2.
Ответ 3
toUpper() не меняет указатель (который принадлежит классу). Он только изменяет данные, на которые указывает rep (которые не принадлежат классу).
Однако, "const" является своего рода гарантией для пользователей вашего класса: если объявлен метод const, кто использует экземпляр вашего класса, он может ожидать, что он не изменится при вызове метода. Я хочу сказать, что если toUpper() изменяет состояние строки, не объявляйте ее const, разрешает ли она С++ или нет.
Ответ 4
Определитель 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
}
}
Ответ 5
Вы не можете изменить значение чего-либо, объявленного как
const char* rep;
или
const char* const rep;
К сожалению, объявление вашего члена const превращается в rep в
char* const rep;
что означает, что вы не можете изменить acutal адрес, но вы можете изменить содержимое, тогда как вы не можете изменить значение.
Чтобы обеспечить уважение const memebrs, сохраните свой буфер const, вам нужно будет создать rep и массив символов или строковый объект, а не указатель символа.