Бьерн Страуструп - Язык программирования С++. Главы 5-7 - Строковый класс

ОГЛАВЛЕНИЕ


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

Теперь можно привести более осмысленный вариант класса 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;
         }