Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Листинг 10.4






 

#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];

}

 

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

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

 

class X

{

//...

X(int);

int m();

friend int f(X&);

};

 

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

 

void g()

{

1.m(); // ошибка

f(1); // f(x(1));

}

 

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

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

Конечно, если inv() действительно обращает матрицу m, а не просто возвращает новую матрицу, обратную m, ей следует быть другом.

При прочих равных условиях выбирайте, чтобы функция была членом: никто не знает, вдруг когда-нибудь кто-то определит операцию преобразования.

Невозможно предсказать, потребуют ли будущие изменения изменить статус объекта. Синтаксис вызова функции члена ясно указывает пользователю, что объект можно изменить; ссылочный параметр является далеко не столь очевидным. Кроме того, выражения в члене могут быть заметно короче выражений в друге. В функции друге надо использовать явный параметр, тогда как в члене можно использовать неявный this. Если только не применяется перегрузка, имена членов обычно короче имен друзей.

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

 






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.