Вход/Регистрация
C++
вернуться

Страустрап Бьярн

Шрифт:

friend ostream amp; operator«„(ostream amp;, string amp;); friend istream amp; operator“»(istream amp;, string amp;);

friend int operator==(string amp; x, char* s) (*return strcmp(x.p-»s, s) == 0; *)

friend int operator==(string amp; x, string amp; y) (*return strcmp(x.p-»s, y.p-»s) == 0; *)

friend int operator!=(string amp; x, char* s) (*return strcmp(x.p-»s, s) != 0; *)

friend int operator!=(string amp; x, string amp; y) (*return strcmp(x.p-»s, y.p-»s) != 0; *)

*);

Конструкторы и деструкторы просты (как обычно):

string::string (* p = new srep; p-»s = 0; p-»n = 1; *)

string::string(char* s) (* p = new srep; p-»s = new char[ strlen(s)+1 ]; strcpy(p-»s, s); p-»n = 1; *)

string::string(string amp; x) (* x.p-»n++; p = x.p; *)

string::~string (* if (–p-»n == 0) (* delete p-»s; delete p; *) *)

Как обычно, операции присваивания очень похожи на контрукторы. Они должны обрабатывать очистку своего первого (лвого) операнда:

string amp; string::operator=(char* s) (* if (p-»n » 1) (* // разъединить себя p-»n–; p = new srep; *) else if (p-»n == 1) delete p-»s;

p-»s = new char[ strlen(s)+1 ]; strcpy(p-»s, s); p-»n = 1; return *this; *)

Благоразумно обеспечить, чтобы присваивание объекта смому себе работало правильно:

string amp; string::operator=(string amp; x) (* x.p-»n++; if (–p-»n == 0) (* delete p-»s; delete p; *) p = x.p; return *this; *)

Операция вывода задумана так, чтобы продемонстрировать применение учета ссылок. Она повторяет каждую вводимую строку (с помощью операции ««, которая определяется позднее):

ostream amp; operator«„(ostream amp; s, string amp; x) (* return s „„ x.p-“s „« « [“ «« x.p-“n «« «]\n“; *)

Операция ввода использует стандартную функцию ввода сивольной строки (#8.4.1).

istream amp; operator»»(istream amp; s, string amp; x) (* char buf[256]; s »» buf; x = buf; cout «„ "echo: " «« x «« «\n“; return s; *)

Для доступа к отдельным символам предоставлена операция индексирования. Осуществляется проверка индекса:

void error(char* p) (* cerr «„ p «« «\n“; exit(1); *)

char amp; string::operator[](int i) (* if (i«0 !! strlen(p-»s)«i) error(„индекс за границами“); return p-»s[i]; *)

Головная программа просто немного опробует действия над строками. Она читает слова со ввода в строки, а потом эти строки печатает. Она продолжает это делать до тех пор, пока не распознает строку done, которая завершает сохранение слов в строках, или пока не встретит конец файла. После этого она печатает строки в обратном порядке и завершается.

main (* string x[100]; int n;

cout «„ „отсюда начнем\n“; for (n = 0; cin“»x[n]; n++) (* string y; if (n==100) error(«слишком много строк»); cout «„ (y = x[n]); if (y=="done") break; *) cout «« «отсюда мы пройдем обратно\n“;

for (int i=n-1; 0«=i; i–) cout «« x[i]; *)

6.10 Друзья и члены

Теперь, наконец, можно обсудить, в каких случаях для доступа к закрытой части определяемого пользователем типа ипользовать члены, а в каких – друзей. Некоторые операции должны быть членами: конструкторы, деструкторы и виртуальные функции (см. следующую главу), но обычно это зависит от выбра.

Рассмотрим простой класс X:

class X (* // ... X(int); int m; friend int f(X amp;); *);

Внешне не видно никаких причин делать f(X amp;) другом дполнительно к члену X::m (или наоборот), чтобы реализовать действия над классом X. Однако член X::m можно вызывать только для «настоящего объекта», в то время как друг f мжет вызываться для объекта, созданного с помощью неявного преобразования типа. Например:

void g (* 1.m; // ошибка f(1); // f(x(1)); *)

Поэтому операция, изменяющая состояние объекта, должна быть членом, а не другом. Для определяемых пользователем тпов операции, требующие в случае фундаментальных типов опранд lvalue (=, *=, ++, *= и т.д.), наиболее естественно оределяются как члены.

И наоборот, если нужно иметь неявное преобразование для всех операндов операции, то реализующая ее функция должна быть другом, а не членом. Это часто имеет место для функций, которые реализуют операции, не требующие при применении к фундаментальным типам lvalue в качестве операндов (+, -, !! и т.д.).

Если никакие преобразования типа не определены, то окзывается, что нет никаких существенных оснований в пользу члена, если есть друг, который получает ссылочный параметр, и наоборот. В некоторых случаях программист может предпочитать один синтаксис вызова другому. Например, оказывается, что большинство предпочитает для обращения матрицы m запись m.inv . Конечно, если inv действительно обращает матрицу m, а не просто возвращает новую матрицу, обратную m, ей следует быть членом.

  • Читать дальше
  • 1
  • ...
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: