C++. Сборник рецептов - [203]

Шрифт
Интервал

В отличие от данного члена с функцией-членом вы не можете сделать то же самое, потому что это бессмысленно. Рассмотрим указатель на функцию, имеющую такую же сигнатуру, как >MyClass::incr (т.е. он возвращает >void и не имеет аргументов).

>void (*pf)();

Теперь попытайтесь присвоить этому указателю адрес функции-члена.

>pf = &MyClass::incr; // He получится

>pf = &obj.incr;      // И это не пройдет

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

См. также

Рецепт 15.1.

15.3. Обеспечение невозможности модификации аргумента в функции

Проблема

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

Решение

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

Пример 15.3. Гарантия невозможности модификации аргументов

>#include

>#include


>void concat(const std::string& s1, // Аргументы объявлены как константное,

> const std::string& s2,            // поэтому не могут быть изменены

> std::string& out) {

> out = s1 + s2;

>}


>int main() {

> std::string s1 = "Cabo ";

> std::string s2 = "Wabo";

> std::string s3;

> concat(s1, s2, s3);

> std::cout << "s1 = " << s1 << '\n';

> std::cout << "s2 = " << s2 << '\n';

> std::cout << "s3 = " << s3 << '\n';

>}

Обсуждение

В примере 15.3 продемонстрировано прямое использование ключевого слова >const. Существует две причины объявления параметров вашей функции с этим ключевым словом, когда вы не планируете их изменять. Во-первых, этим вы сообщаете о своих намерениях читателям вашего программного кода. Объявляя параметр как >const, вы фактически говорите, что он является входным параметром. Это позволяет пользователям вашей функции писать программный код в расчете на то, что эти значения не будут изменены. Во-вторых, это позволяет компилятору запретить любые модифицирующие операции на тот случай, если вы случайно их используете. Рассмотрим небезопасную версию >concat из примера 15 3.

>void concatUnsafe(std::string& s1,

> std::string& s2 std::string& out) {

> out = s1 += s2; // Ну вот, записано значение в s1

>}

Несмотря на мою привычку тщательно подходить к кодированию программ, я сделал глупую ошибку и написал >+= вместо >+. В результате при вызове >concatUnsafe будут модифицированы аргументы >out и >s1, что может оказаться сюрпризом для пользователя, который едва ли рассчитывает на модификацию одной из исходных строк.

Спасти может >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, или, другими словами, используя их, вы можете сослаться на какую-нибудь другую строку, изменяя значение указателя, но не значение, на которое он ссылается. Ничто не может помешать вам, например, сделать следующее.


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


MFC и OpenGL

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Симуляция частичной специализации

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Обработка событий в С++

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.