Язык программирования C++ от Страуструпа

       

Строковый класс


Теперь можно привести более осмысленный вариант класса string. В нем подсчитывается число ссылок на строку, чтобы минимизировать копирование, и используются как константы стандартные строки C++.

#include <iostream.h>

#include <string.h>

class string {

  struct srep {

     char* s;                       // указатель на строку

     int n;                         // счетчик числа ссылок

     srep() { n = 1; }

  };

  srep *p;

  public:

     string(const char *);          // string x = "abc"

     string();                      // string x;



     string(const string &);        // string x = string ...

     string& operator=(const char *);

     string& operator=(const string &);

     ~string();

     char& operator[](int i);

     friend ostream& operator<<(ostream&, const string&);

     friend istream& operator>>(istream&, string&);

     friend int operator==(const string &x, const char *s)

       { return strcmp(x.p->s,s) == 0; }

     friend int operator==(const string &x, const string &y)

       { return strcmp(x.p->s,y.p->s) == 0; }

     friend int operator!=(const string &x, const char *s)

       { return strcmp(x.p->s,s) != 0; }

     friend int operator!=(const string &x, const string &y)

       { return strcmp(x.p->s,y.p->s) != 0; }

};

Конструкторы и деструкторы тривиальны:

string::string()

{

  p = new srep;

  p->s = 0;

}

string::string(const string& x)

{

  x.p->n++;

  p = x.p;

}

string::string(const char* s)

{

  p = new srep;

  p->s = new char[ strlen(s)+1 ];

  strcpy(p->s, s);

}

string::~string()

{

  if (--p->n == 0) {

     delete[]  p->s;

     delete p;

  }

}

Как и всегда операции присваивания похожи на конструкторы. В них нужно позаботиться об удалении первого операнда, задающего левую часть присваивания:

string& string::operator=(const char* s)

{

  if (p->n > 1) {                  // отсоединяемся от старой строки


     p->n--;

     p = new srep;

  }

  else     // освобождаем строку со старым значением

     delete[] p->s;

  p->s = new char[ strlen(s)+1 ];

  strcpy(p->s, s);

  return *this;

}

string& string::operator=(const string& x)

{

  x.p->n++;                        // защита от случая ``st = st''

  if (--p->n == 0) {

     delete[] p->s;

     delete p

  }

  p = x.p;

  return *this;

}

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

ostream& operator<<(ostream& s, const string& x)

{

  return s << x.p->s << " [" << x.p->n << "]\n";

}

Операция ввода происходит с помощью стандартной функции ввода символьной строки ($$10.3.1):

istream& operator>>(istream& s, string& x)

{

  char buf[256];

  s >> buf;                        // ненадежно: возможно переполнение buf

                                   // правильное решение см. в $$10.3.1

  x = buf;

  cout << "echo: " << x << '\n';

  return s;

}

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

void error(const char* p)

{

  cerr << p << '\n';

  exit(1);

}

char& string::operator[](int i)

{

if (i<0 || strlen(p->s)<i) error("недопустимое значение индекса");

  return p->s[i];

}

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

int main()

{

  string x[100];

  int n;

  cout << " здесь начало \n";

  for ( n = 0; cin>>x[n]; n++) {

     if (n==100) {

       error("слишком много слов");

       return 99;

     }

     string y;

     cout << (y = x[n]);

     if (y == "done") break;

  }

  cout << "теперь мы идем по словам в обратном порядке \n";

  for (int i=n-1; 0<=i; i--) cout << x[i];

     return 0;

}


Содержание раздела