C++. Сборник рецептов - [205]
> ~String() {delete buf_;}
> String& append(const String& s);
> size_t length() const;
> const char* data() const;
> String& operator=(const String& orig);
> // ...
>};
>String operator+(const String& lhs, const String& rhs) {
> String tmp(lhs); // Сконструировать временный объект с помощью
> // конструктора копирования
> tmp.append(rhs); // Использовать функцию-член для выполнения реальной
> // работы
> return(tmp); // Возвратить временный объект
>}
>int main() {
> String s1("banana ");
> String s2("rancher");
> String s3, s4, s5, s6;
> s3 = s1 + s2; // Работает хорошо, но с сюрпризами
> s4 = s1 + "rama"; // Автоматически конструируется "rama", используя
> // конструктор String(const char*)
> s5 = "ham " + s2; // Круто, то же самое можно делать даже
> s6 = s1 + "rama " + s2; // с другим операндом
> std::cout << "s3 = " << s3.data() << '\n';
> std::cout << "s4 = " << s4.data() << '\n';
> std::cout << "s5 = " << s5.data() << '\n';
> std::cout << "s6 = " << s6.data() << '\n';
>}
Независимый оператор объявляется и определяется подобно оператору функции-члена. В примере 15.5 я мог бы реализовать >operator+
как функцию-член, объявляя ее следующим образом.
>String operator+(const String& rhs);
В большинстве случаев это будет работать одинаково, независимо от того, определяется ли >operator+
как функция-член или нет, однако существует, по крайней мере, две причины, по которым желательно реализовать его не как функцию-член. Первая причина концептуальная, имеет ли смысл иметь оператор, который возвращает новый, отличный от других объект? >operator+
, реализованный как функция-член, не проверяет и не изменяет состояние объекта. Это служебная функция общего назначения, которая в данном случае работает со строками типа >String
и, следовательно, не должна являться функцией членом.
Вторая причина техническая. При использовании оператора-члена вы не сможете выполнить следующую операцию (из приведенного выше примера).
>s5 = "ham " + s2;
Это не сработает, потому что символьная строка не имеет >operator+
, который принимает >String
в качестве параметра. С другой стороны, если вы определили независимый >operator+
, который принимает два параметра типа >String
, ваш компилятор проверит наличие в классе >String
конструктора, принимающего >const char*
в качестве аргумента (или любой другой тип, который вы используете совместно с >String
), и сконструирует временный объект на этапе выполнения. Поэтому приведенная выше строка эквивалентна следующей.
>s5 = String("ham ") + s2;
Компилятор позволяет вам немного сэкономить ваши действия и не вводить несколько символов за счет поиска и вызова соответствующего конструктора.
Перегрузка операторов сдвига потоков влево и вправо (><<
и >>>
) также требует применения операторов не-членов. Например, для записи нового объекта в поток, используя сдвиг влево, вам придется следующим образом объявить >operator<<
:
>ostream& operator<<(ostream& str, const MyClass& obj);
Конечно, вы можете создать подкласс одного из классов потока стандартной библиотеки и добавить все необходимые вам операторы сдвига влево, но будет ли такое решение действительно удачным? При таком решении только тот программный код, который использует ваш новый класс потока, сможет записывать в него объекты вашего специального класса. Если вы используете независимый оператор, любой программный код в том же самом пространстве имен сможет без проблем записать ваш объект в >ostream
(или считать его из >istream
).
15.6. Инициализация последовательности значениями, разделяемыми запятыми
Требуется инициализировать последовательность набором значений, разделяемых запятыми, подобно тому как это делается для встроенных массивов.
При инициализации стандартных последовательностей (таких как >vector
и >list
) можно использовать синтаксис с запятыми, определяя вспомогательный класс и перегружая оператор запятой, как это продемонстрировано в примере 15.6.
Пример 15.6. Вспомогательные классы для инициализации стандартных последовательностей с применением синтаксиса с запятыми
>#include
>#include
>#include
>#include
>using namespace std;
>template
>struct comma helper {
> typedef typename Seq_T::value_type value_type;
> explicit comma_helper(Seq_T& x) : m(x) {}
> comma_helper& operator=(const value_type& x) {
> m.clear();
> return operator+=(x);
> }
> comma_helper& operator+=(const value_type& x) {
> m.push_back(x);
> return *this;
> }
> Seq_T& m;
>};
>template
>comma_helper
> return comma_helper
>}
>template
>comma_helper
> h += x;
> return h;
>}
>int main() {
> vector v;
> int a = 2;
> int b = 5;
> initialize(v) = 0, 1, 1, a, 3, b, 8, 13;
> cout << v[3] << endl; // выдает 2
> system("pause");
> return EXIT_SUCCESS;
>}
Часто стандартные последовательности инициализируются путем вызова несколько раз функции-члена >push_back
. Поскольку это приходится делать не так уж редко, я написал функцию >initialize
, которая помогает избавиться от этого скучного занятия, позволяя выполнять инициализацию значениями, разделяемыми запятыми, подобно тому как это делается во встроенных массивах.
Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.
Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.