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

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

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

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

struct string {

char* p;

int size; // размер вектора, на который указывает p

string(int size) { p = new char[size=sz]; }

~string() { delete p; }

string& operator=(const string&);

string(const string&);

};

string::string(const string& a)

{

p=new char[size=sz];

strcpy(p,a.p);

}

Инициализация объекта типа X происходит с помощью конструктора

X(const X&). Мы не перестаем повторять, что присваивание и

инициализация являются разными операциями. Особенно это важно в тех

случаях, когда определен деструктор. Если в классе X есть нетривиальный

деструктор, например, производящий освобождение объекта в свободной

памяти, вероятнее всего, в этом классе потребуется полный набор

функций, чтобы избежать копирования объектов по членам:

class X {

// ...

X(something); // конструктор, создающий объект

X(const X&); // конструктор копирования

operator=(const X&); // присваивание:

// удаление и копирование

~X(); // деструктор, удаляющий объект

};

Есть еще два случая, когда приходится копировать объект:

передача параметра функции и возврат ею значения. При передаче

параметра неинициализированная переменная, т.е. формальный параметр

инициализируется. Семантика этой операции идентична другим видам

инициализации. Тоже происходит и при возврате функцией значения,

хотя этот случай не такой очевидный. В обоих случаях используется

конструктор копирования:

string g(string arg)

{

return arg;

}

main()

{

string s = "asdf";

s = g(s);

}

Очевидно, после вызова g() значение s должно быть "asdf". Не трудно

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

конструктор копирования для string. Для получения еще одной копии

значения s по выходе из g() нужен еще один вызов конструктора

string(const string&). На этот раз инициализируется временная

переменная, которая затем присваивается s. Для оптимизации одну,

но не обе, из подобных операций копирования можно убрать. Естественно,

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

надлежащим образом деструктором string::~string() (см. $$R.12.2).

Если в классе X операция присваивания X::operator=(const X&)

и конструктор копирования X::X(const X&) явно не заданы программистом,

недостающие операции будут созданы транслятором. Эти созданные

функции будут копировать по членам для всех членов класса X. Если

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

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

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

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

вызываться соответствующим образом:

class Record {

string name, address, profession;

// ...

};

void f(Record& r1)

{

Record r2 = r1;

}

Здесь для копирования каждого члена типа string из объекта r1

будет вызываться string::operator=(const string&). В нашем первом

и неполноценном варианте строковый класс имеет член-указатель

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

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

о таких ситуациях.

7.7 Индексация

Операторная функция operator[] задает для объектов классов

интерпретацию индексации. Второй параметр этой функций (индекс) может

иметь произвольный тип. Это позволяет, например, определять

ассоциативные массивы. В качестве примера можно переписать

определение из $$2.3.10, где ассоциативный массив использовался

в небольшой программе, подсчитывающей число вхождений слов в файле.

Там для этого использовалась функция. Мы определим настоящий тип

ассоциативного массива:

class assoc {

struct pair {

char* name;

int val;

};

pair* vec;

int max;

int free;

assoc(const assoc&); // предотвращает копирование

assoc& operator=(const assoc&); // предотвращает копирование

public:

assoc(int);

int& operator[](const char*);

void print_all();

};

В объекте assoc хранится вектор из структур pair размером max.

В переменной free хранится индекс первого свободного элемента

вектора.

Чтобы предотвратить копирование объектов assoc, конструктор

копирования и операция присваивания описаны как частные. Конструктор

выглядит так:

assoc::assoc(int s)

{

max = (s<16) ? 16 : s;

free = 0;

vec = new pair[max];

}

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

что и в $$2.3.10. Но теперь, если вектор переполняется, объект

assoc увеличивается:

#include <string.h>

int& assoc::operator[](const char* p)

/*

работает с множеством пар (структур pair):

проводит поиск p, возвращает ссылку на

целое значение из найденной пары,

создает новую пару, если p не найдено

*/

{

register pair* pp;

for (pp=&vec[free-1]; vec<=pp; pp-- )

if (strcmp(p,pp->name) == 0) return pp->val;

if (free == max) { //переполнение: вектор увеличивается

pair* nvec = new pair[max*2];

for (int i=0; i<max; i++) nvec[i] = vec[i];

delete vec;

vec = nvec;

max = 2*max;

}

pp = &vec[free++];

pp->name = new char[strlen(p)+1];

strcpy(pp->name,p);

pp->val = 0; // начальное значение = 0

return pp->val;

}

Поскольку представление объекта assoc скрыто от пользователя, нужно

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

будет показано как определить настоящий итератор для такого объекта.

Здесь же мы ограничимся простой функцией печати:

void assoc::print_all()

{

for (int i = 0; i<free; i++)

cout << vec[i].name << ": " << vec[i].val << '\n';

}

Наконец, можно написать тривиальную программу:

main() // подсчет числа вхождений во входной

// поток каждого слова

{

const MAX = 256; // больше длины самого длинного слова

char buf[MAX];

assoc vec(512);

while (cin>>buf) vec[buf]++;

vec.print_all();

}

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

легко опровергнуть. Решить возникающую здесь проблему предлагается

в упражнении $$7.14 [20]. Дальнейшее развитие понятие ассоциативного

массива получит в $$8.8.

Функция operator[]() должна быть членом класса. Отсюда следует,

что эквивалентность x[y] == y[x] может не выполняться, если

x объект класса. Обычные отношения эквивалентности, справедливые

для операций со встроенными типами, могут не выполняться для

пользовательских типов ($$7.2.2, см. также $$7.9).

7.8 Вызов функции

Вызов функции, т.е. конструкцию выражение(список-выражений), можно

рассматривать как бинарную операцию, в которой выражение является

левым операндом, а список-выражений - правым. Операцию вызова

можно перегружать как и другие операции. В функции operator()()

список фактических параметров вычисляется и проверяется по типам

согласно обычным правилам передачи параметров. Перегрузка операции

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

только одна операция, а также для тех типов, одна из операций над

которыми имеет настолько важное значение, что все остальные в

большинстве случаев можно не учитывать.

Мы не дали определения итератора для ассоциативного массива

типа assoc. Для этой цели можно определить специальный класс

assoc_iterator, задача которого выдавать элементы из assoc в некотором

порядке. В итераторе необходимо иметь доступ к данным, хранимым

в assoc, поэтому он должен быть описан как friend:

class assoc {

friend class assoc_iterator;

pair* vec;

int max;

int free;

public:

assoc(int);

int& operator[](const char*);

};

Итератор можно определить так:

class assoc_iterator {

const assoc* cs; // массив assoc

int i; // текущий индекс

public:

assoc_iterator(const assoc& s) { cs = &s; i = 0; }

pair* operator()()

{ return (i<cs->free)? &cs->vec[i++] : 0; }

};

Массив assoc объекта assoc_iterator нужно инициализировать, и при каждом

обращении к нему с помощью операторной функции () будет возвращаться

указатель на новую пару (структура pair) из этого массива. При достижении

конца массива возвращается 0:

main() // подсчет числа вхождений во входной

// поток каждого слова

{

const MAX = 256; // больше длины самого длинного слова

char buf[MAX];

assoc vec(512);

while (cin>>buf) vec[buf]++;

assoc_iterator next(vec);

pair* p;

while ( p = next(vec) )

cout << p->name << ": " << p->val << '\n';

}

Итератор подобного вида имеет преимущество перед набором

функций, решающим ту же задачу: итератор может иметь собственные

частные данные, в которых можно хранить информацию о ходе итерации.

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

итераторов одного типа.

Конечно, использование объектов для представления итераторов

непосредственно никак не связано с перегрузкой операций. Одни

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

first(), next() и last(), другим больше нравится перегрузка операции

++ , которая позволяет получить итератор, используемый как указатель

(см. $$8.8). Кроме того, операторная функция operator() активно

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

Функция operator() должна быть функцией-членом.

7.9 Косвенное обращение

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

постфиксную операцию. Это значит, если есть класс

class Ptr {

// ...

X* operator->();

};

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

X также, как для этой цели используются указатели:

void f(Ptr p)

{

p->m = 7; // (p.operator->())->m = 7

}

Превращение объекта p в указатель p.operator->() никак не зависит от

члена m, на который он указывает. Именно по этой причине operator->()

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

синтаксических обозначений, так что имя члена по-прежнему должно

идти после -> :

void g(Ptr p)

{

X* q1 = p->; // синтаксическая ошибка

X* q2 = p.operator->(); // нормально

}

Перегрузка операции -> прежде всего используется для создания

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

указатели позволяют проводить некоторые операции при каждом обращении

к указуемому объекту с их помощью. Например, можно определить класс

RecPtr для организации доступа к объектам класса Rec, хранимым на

диске. Параметром конструктора RecPtr является имя, которое будет

использоваться для поиска объекта на диске. При обращении к объекту

с помощью функции RecPtr::operator->() он переписывается в основную

память, а в конце работы деструктор RecPtr записывает измененный

объект обратно на диск.

class RecPtr {

Rec* in_core_address;

const char* identifier;

// ...

public:

RecPtr(const char* p)

: identifier(p) { in_core_address = 0; }

~RecPtr()

{ write_to_disc(in_core_address,identifier); }

Rec* operator->();

};

Rec* RecPtr::operator->()

{

if (in_core_address == 0)

in_core_address = read_from_disc(identifier);

return in_core_address;

}

Использовать это можно так:

main(int argc, const char* argv)

{

for (int i = argc; i; i--) {

RecPtr p(argv[i]);

p->update();

}

}

На самом деле, тип RecPtr должен определяться как шаблон типа

(см. $$8), а тип структуры Record будет его параметром. Кроме

того, настоящая программа будет содержать обработку ошибок и

взаимодействие с диском будет организовано не столь примитивно.

Для обычных указателей операция -> эквивалентна операциям,

использующим * и []. Так, если описано

Y* p;

то выполняется соотношение

p->m == (*p).m == p[0].m

Как всегда, для определенных пользователем операций такие соотношения

не гарантируются. Там, где все-таки такая эквивалентность требуется,

ее можно обеспечить:

class X {

Y* p;

public:

Y* operator->() { return p; }

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

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

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

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