C++. Сборник рецептов
Шрифт:
Спасти может
const
. Создайте новую функцию concatSafe
, объявите переменные константными, как показано в примере 15.3, и функция не будет откомпилирована. void concatSafe(const std::string& s1,
const std::string& s2, std::string& out) {
out = s1 += s2; // Теперь вы получите ошибку компиляции
}
concatSafе
гарантирует неизменяемость значений в s1
и s2
. Эта функция делает еще кое-что: она позволяет
void myFunc(const std::string& s) { // Обратите внимание, что s является
// константной переменной
std::string dest;
std::string tmp = "foo";
concatUnsafe(s, tmp, dest); // Ошибка: s - константная переменная
// Выполнить какие-то действия с dest...
}
В данном случае функция
myFunc
не будет откомпилирована, потому что concatUnsafe
не обеспечивает const
'антность myFunc
. myFunc
гарантирует внешнему миру, что она не будет модифицировать содержимое s
, т.е. все действия с s
внутри тела myFunc
не должны нарушать это обещание. Конечно, вы можете обойти это ограничение, используя оператор const_cast
и тем самым освобождаясь от константности, но такой подход ненадежен, и его следует избегать. В этой ситуации concatSafe
будет компилироваться и выполняться нормально. Указатели вносят темные штрихи в розовую картину
const
. Когда вы объявляете переменную-указатель как параметр, вы имеет дело с двумя объектами: самим адресом и то, на что ссылается этот адрес. C++ позволяет использовать const
для ограничения действий по отношению к обоим объектам. Рассмотрим еще одну функцию конкатенации, которая использует указатели. void concatUnsafePtr(std::string* ps1,
std::string* ps2, std::string* pout) {
*pout = *ps1 + *ps2;
}
Здесь такая же проблема, как в примере с
concatUnsafe
, описанном ранее. Добавьте const
для гарантии невозможности обновления исходных строк. void concatSaferPtr(const std::string* ps1,
const std::string* ps2, std::string* pout) {
*pout = *ps1 + *ps2;
}
Отлично, теперь вы не можете изменить
*ps1
и *ps2
. Но вы по-прежнему можете изменить ps1
и ps2
, или, другими словами, используя их, вы можете сослаться на какую-нибудь другую строку, изменяя значение указателя, но не значение, на которое он ссылается. Ничто не может помешать вам, например, сделать следующее. void concatSaferPtr(const std:string* ps1,
const std::string* ps2, std::string* pout) {
ps1 = pout; // Ух!
*pout = *ps1 + *ps2;
}
Предотвратить
const
. void concatSafestPtr(const std::string* const ps1,
const std::string* const ps2, std::string* pout) {
*pout = *ps1 + *ps2;
}
Применение
const
по обе стороны звездочки делает вашу функцию максимально надежной. В этом случае вы ясно показываете свои намерения пользователям вашей функции, и ваша репутация не пострадает в случае описки. См. также
Рецепт 15.4.
15.4. Обеспечение невозможности модификации своих объектов в функции-члене
Проблема
Требуется вызывать функции -члены для константного объекта, но ваш компилятор жалуется на то, что он не может преобразовать тип используемого вами объекта из константного в неконстантный.
Решение
Поместите ключевое слово
const
справа от имени функции-члена при ее объявлении в классе и при ее определении. Пример 15.4 показывает, как это можно сделать Пример 15.4. Объявление функции-члена константной
#include <iostream>
#include <string>
class RecordSet {
public:
bool getFieldVal(int i, std::string& s) const;
// ...
};
bool RecordSet::getFieldVal(int i, std::string& s) const {
// Здесь нельзя модифицировать никакие неизменяемые
// данные-члены (см. обсуждение)
}
void displayRecords(const RecordSet& rs) {
// Здесь вы можете вызывать только константные функции-члены
// для rs
}
Обсуждение
Добавление концевого
const
в объявление члена и в его определение заставляет компилятор более внимательно отнестись к тому, что делается с объектом внутри тела члена. Константным функциям-членам не разрешается выполнять неконстантные операции с данными-членами. Если такие операции присутствуют, компиляция завершится неудачно. Например, если бы в RecordSet::getFieldVal
я обновил счетчик-член, эта функция не была бы откомпилирована (в предположении, что getFieldCount_
является переменной-членом класса RecordSet
). bool RecordSet::getFieldVal(int i, std::string& s) const {
++getFieldCount_; // Ошибка: константная функция-член не может
// модифицировать переменную-член
// ...
}
Это может также помочь обнаружить более тонкие ошибки, подобно тому, что делает
const
в роли квалификатора переменной (см. рецепт 15.3). Рассмотрим следующую глупую ошибку.
Поделиться:
Популярные книги
Печать Пожирателя
1. Пожиратель
Фантастика:
попаданцы
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Привет из Загса. Милый, ты не потерял кольцо?
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Мастер 2
2. Мастер
Фантастика:
фэнтези
городское фэнтези
попаданцы
технофэнтези
4.50
рейтинг книги
Нечто чудесное
2. Романтическая серия
Любовные романы:
исторические любовные романы
9.43
рейтинг книги
Клан
2. Долгий путь домой
Фантастика:
боевая фантастика
космическая фантастика
5.60
рейтинг книги
Имя нам Легион. Том 3
3. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Запасная дочь
Фантастика:
фэнтези
6.40
рейтинг книги
Убивать чтобы жить 7
7. УЧЖ
Фантастика:
героическая фантастика
космическая фантастика
рпг
5.00
рейтинг книги
У врага за пазухой
5. Оголенные чувства
Любовные романы:
остросюжетные любовные романы
эро литература
5.00
рейтинг книги
Кодекс Охотника. Книга XXI
21. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Генерал Скала и ученица
2. Генерал Скала и Лидия
Любовные романы:
любовно-фантастические романы
6.30
рейтинг книги
Оцифрованный. Том 1
1. Линкор Михаил
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Его маленькая большая женщина
Любовные романы:
современные любовные романы
эро литература
8.78
рейтинг книги
Хуррит
Фантастика:
героическая фантастика
попаданцы
альтернативная история
5.00