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). Рассмотрим следующую глупую ошибку.
Поделиться:
Популярные книги
Начальник милиции. Книга 5
5. Начальник милиции
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Папина дочка
4. Самбисты
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Возвышение Меркурия. Книга 8
8. Меркурий
Фантастика:
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
АллатРа
Научно-образовательная:
психология
история
философия
обществознание
физика
6.25
рейтинг книги
Золушка вне правил
Любовные романы:
любовно-фантастические романы
6.83
рейтинг книги
Возвышение Меркурия. Книга 5
5. Меркурий
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
На Ларэде
3. Лэрн
Фантастика:
фэнтези
героическая фантастика
стимпанк
5.00
рейтинг книги
Барон Дубов 6
6. Его Дубейшество
Фантастика:
юмористическое фэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Ротмистр Гордеев
1. Ротмистр Гордеев
Фантастика:
фэнтези
попаданцы
альтернативная история
5.00
рейтинг книги
Кодекс Крови. Книга VI
6. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Лучше подавать холодным
4. Земной круг. Первый Закон
Фантастика:
фэнтези
8.45
рейтинг книги
Звездная Кровь. Изгой
1. Звездная Кровь. Изгой
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Крестоносец
7. Помещик
Фантастика:
героическая фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Сердце Дракона. Том 12
12. Сердце дракона
Фантастика:
фэнтези
героическая фантастика
боевая фантастика
7.29