Вот довольно реалистичный пример класса string. В нем производится учет ссылок на строку с целью минимизировать копирование и в качестве констант применяются стандартные символьные строки C++.
#include #include class string { struct srep { char* s; // указатель на данные int n; // счетчик ссылок }; srep *p; public: string(char *); // string x = "abc" string(); // string x; string(string &); // string x = string ... string& operator=(char *); string& operator=(string &); ~string(); char& operator[](int i); friend ostream& operator<<(ostream&, string&); friend istream& operator>>(istream&, string&); friend int operator==(string& x, char* s) {return strcmp(x.p->s, s) == 0; } friend int operator==(string& x, string& y) {return strcmp(x.p->s, y.p->s) == 0; } friend int operator!=(string& x, char* s) {return strcmp(x.p->s, s) != 0; } friend int operator!=(string& x, string& 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& x) { x.p->n++; p = x.p; } string::~string() { if (--p->n == 0) { delete p->s; delete p; } }
Как обычно, операции присваивания очень похожи на конструкторы. Они должны обрабатывать очистку своего первого (левого) операнда:
string& 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& string::operator=(string& x) { x.p->n++; if (--p->n == 0) { delete p->s; delete p; } p = x.p; return *this; }
Операция вывода задумана так, чтобы продемонстрировать применение учета ссылок. Она повторяет каждую вводимую строку (с помощью операции <<, которая определяется позднее):
ostream& operator<<(ostream& s, string& x) { return s << x.p->s << " [" << x.p->n << "]\n"; }
Операция ввода использует стандартную функцию ввода символьной строки (см. этот пункт).
istream& operator>>(istream& s, string& x) { char buf[256]; s >> buf; x = buf; cout << "echo: " << x << "\n"; return s; }
Для доступа к отдельным символам предоставлена операция индексирования. Осуществляется проверка индекса:
void error(char* p) { cerr << p << "\n"; exit(1); } char& string::operator[](int i) { if (i<0 || strlen(p->s)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]; }
8 8 8
|