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