Главная » Просмотр файлов » Бьерн Страуструп

Бьерн Страуструп (947334), страница 42

Файл №947334 Бьерн Страуструп (Стpаустpуп - Книга о C++) 42 страницаБьерн Страуструп (947334) страница 422013-09-15СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 42)

Y& operator*() { return *p; }

Y& operator[](int i) { return p[i]; }

};

Если в вашем классе определено более одной подобной операции,

разумно будет обеспечить эквивалентность, точно так же, как разумно

предусмотреть для простой переменной x некоторого класса, в котором

есть операции ++, += = и +, чтобы операции ++x и x+=1 были

эквивалентны x=x+1.

Перегрузка -> как и перегрузка [] может играть важную роль для

целого класса настоящих программ, а не является просто экспериментом

ради любопытства. Дело в том, что в программировании понятие

косвенности является ключевым, а перегрузка -> дает ясный, прямой

и эффективный способ представления этого понятия в программе.

Есть другая точка зрения на операцию ->, как на средство задать

в С++ ограниченный, но полезный вариант понятия делегирования

(см. $$12.2.8 и 13.9).

7.10 Инкремент и декремент

Если мы додумались до "хитрых указателей", то логично попробовать

переопределить операции инкремента ++ и декремента -- , чтобы

получить для классов те возможности, которые эти операции дают для

встроенных типов. Такая задача особенно естественна и необходима, если

ставится цель заменить тип обычных указателей на тип "хитрых указателей",

для которого семантика остается прежней, но появляются некоторые

действия динамического контроля. Пусть есть программа с распространенной

ошибкой:

void f1(T a) // традиционное использование

{

T v[200];

T* p = &v[10];

p--;

*p = a; // Приехали: `p' настроен вне массива,

// и это не обнаружено

++p;

*p = a; // нормально

}

Естественно желание заменить указатель p на объект класса

CheckedPtrToT, по которому косвенное обращение возможно только

при условии, что он действительно указывает на объект. Применять

инкремент и декремент к такому указателю будет можно только в том

случае, что указатель настроен на объект в границах массива и

в результате этих операций получится объект в границах того же

массива:

class CheckedPtrToT {

// ...

};

void f2(T a) // вариант с контролем

{

T v[200];

CheckedPtrToT p(&v[0],v,200);

p--;

*p = a; // динамическая ошибка:

// `p' вышел за границы массива

++p;

*p = a; // нормально

}

Инкремент и декремент являются единственными операциями в С++,

которые можно использовать как постфиксные и префиксные операции.

Следовательно, в определении класса CheckedPtrToT мы должны

предусмотреть отдельные функции для префиксных и постфиксных операций

инкремента и декремента:

class CheckedPtrToT {

T* p;

T* array;

int size;

public:

// начальное значение `p'

// связываем с массивом `a' размера `s'

CheckedPtrToT(T* p, T* a, int s);

// начальное значение `p'

// связываем с одиночным объектом

CheckedPtrToT(T* p);

T* operator++(); // префиксная

T* operator++(int); // постфиксная

T* operator--(); // префиксная

T* operator--(int); // постфиксная

T& operator*(); // префиксная

};

Параметр типа int служит указанием, что функция будет вызываться

для постфиксной операции. На самом деле этот параметр является

искусственным и никогда не используется, а служит только для различия

постфиксной и префиксной операции. Чтобы запомнить, какая версия

функции operator++ используется как префиксная операция, достаточно

помнить, что префиксной является версия без искусственного параметра,

что верно и для всех других унарных арифметических и логических

операций. Искусственный параметр используется только для "особых"

постфиксных операций ++ и --.

С помощью класса CheckedPtrToT пример можно записать так:

void f3(T a) // вариант с контролем

{

T v[200];

CheckedPtrToT p(&v[0],v,200);

p.operator--(1);

p.operator*() = a; // динамическая ошибка:

// `p' вышел за границы массива

p.operator++();

p.operator*() = a; // нормально

}

В упражнении $$7.14 [19] предлагается завершить определение класса

CheckedPtrToT, а другим упражнением ($$9.10[2]) является

преобразование его в шаблон типа, в котором для сообщений о

динамических ошибках используются особые ситуации. Примеры использования

операций ++ и -- для итераций можно найти в $$8.8.

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;

}

7.12 Друзья и члены

В заключении можно обсудить, когда при обращении в закрытую часть

пользовательского типа стоит использовать функции-члены, а когда

функции-друзья. Некоторые функции, например конструкторы, деструкторы

и виртуальные функции ($$R.12), обязаны быть членами, но для других

есть возможность выбора. Поскольку, описывая функцию как член, мы

не вводим нового глобального имени, при отсутствии других доводов

следует использовать функции-члены.

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

class X {

// ...

X(int);

int m1();

int m2() const;

friend int f1(X&);

friend int f2(const X&);

friend int f3(X);

};

Вначале укажем, что члены X::m1() и X::m2() можно вызывать только

для объектов класса X. Преобразование X(int) не будет применяться

к объекту, для которого вызваны X::m1() или X::m2():

void g()

{

1.m1(); // ошибка: X(1).m1() не используется

1.m2(); // ошибка: X(1).m2() не используется

}

Глобальная функция f1() имеет то же свойство ($$4.6.3), поскольку

ее параметр - ссылка без спецификации const. С функциями f2() и

f3() ситуация иная:

void h()

{

f1(1); // ошибка: f1(X(1)) не используется

f2(1); // нормально: f2(X(1));

f3(1); // нормально: f3(X(1));

}

Следовательно операция, изменяющая состояние объекта класса,

должна быть членом или глобальной функцией с параметром-ссылкой

без спецификации const. Операции над основными типами, которые

требуют в качестве операндов адреса (=, *, ++ и т.д.),

для пользовательских типов естественно определять как члены.

Обратно, если требуется неявное преобразование типа для всех

операндов некоторой операции, то реализующая ее функция должна

быть не членом, а глобальной функцией и иметь параметр типа ссылки

со спецификацией const или нессылочный параметр. Так обычно обстоит

дело с функциями, реализующими операции, которые для основных

типов не требуют адресов в качестве операндов (+, -, || и т.д.).

Если операции преобразования типа не определены, то нет

неопровержимых доводов в пользу функции-члена перед функцией-другом

с параметром-ссылкой и наоборот. Бывает, что программисту просто

одна форма записи вызова нравится больше, чем другая.

Например, многим для обозначения функции обращения матрицы m больше

нравится запись inv(m), чем m.inv(). Конечно, если функция

inv() обращает саму матрицу m, а не возвращает новую, обратную m,

матрицу, то inv() должна быть членом.

При всех прочих равных условиях лучше все-таки остановиться

на функции-члене. Можно привести такие доводы. Нельзя гарантировать,

что когда-нибудь не будет определена операция обращения. Нельзя во

всех случаях гарантировать, что будущие изменения не повлекут за

собой изменения в состоянии объекта. Запись вызова функции-члена

ясно показывает программисту, что объект может быть изменен, тогда

как запись с параметром-ссылкой далеко не столь очевидна. Далее,

выражения допустимые в функции-члене могут быть существенно

короче эквивалентных выражений в глобальной функции. Глобальная

функция должна использовать явно заданные параметры, а в

Характеристики

Тип файла
Документ
Размер
4,26 Mb
Тип материала
Учебное заведение
Неизвестно

Список файлов книги

Свежие статьи
Популярно сейчас
Почему делать на заказ в разы дороже, чем купить готовую учебную работу на СтудИзбе? Наши учебные работы продаются каждый год, тогда как большинство заказов выполняются с нуля. Найдите подходящий учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
6374
Авторов
на СтудИзбе
309
Средний доход
с одного платного файла
Обучение Подробнее