Добавил:
t.me Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

2 семестр / Литература / Язык программирования С++. Краткий курс. Страуструп

.pdf
Скачиваний:
9
Добавлен:
16.07.2023
Размер:
31.34 Mб
Скачать

80

Глава 4. Классы

 

bool

operator!=(complex

а,

(

 

 

 

return ! (а==Ь);

 

complex

Ь)

//

Неравенство

complex

11 ...

sqr t(complex);

11

Определение

в

другом

месте

Использоваться

класс

complex

может

следующим

образом:

void

f(complex

z)

 

(

 

 

 

 

 

 

complex

а

(2. 3);

11 Построение

 

complex

Ь

( 1/а};

 

 

complex

с

(a+z*complex(l,2.3));

 

11 ...

 

 

 

 

if

!=

Ь)

 

 

с=

-(Ь/а}+2*Ь;

 

(2.3,0.0}

из

2.3

Компилятор

преобразует

операторы

для

работы

с

числами

complex

в

соот­

ветствующие

вызовы

функций.

Например,

с!

означает

operator

!= (с,

Ь),

а

1

/а означает opera tor / (complex {1}, а) .

 

Пользовательские операторы ("перегруженные

операторы")

должны

ис­

пользоваться

разумно

и

осторожно.

Синтаксис

операторов

фиксируется

язы­

ком,

так

что

вы

не

можете

определить

унарный

оператор

/.

Кроме

того,

не­

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

вы

можете переопределить+ так, чтобы он работал для int как вычитание.

 

не

4.2.2.

Контейнер

Контейнер представляет собой объект, содержащий коллекцию

элементов.

Мы называем класс Vector контейнером,

потому что объекты типа Vector

являются контейнерами. Как определено в

§2.3, Vector является

продуман­

ным

контейнером

douЫe:

он

прост

для

понимания,

устанавливает

полезный

инвариант

(§3.5.2),

обеспечивает

проверку

выхода

обращения

к

элементу

за

диапазон

(§3.5.1)

и

обеспечивает

функцию

size

(),что

позволяет

выполнять

обход

его

элементов.

Однако

у

него

есть

фатальный

недостаток:

он выделяет

память

для

элементов,

используя

оператор

new,

но

никогда

ее

не

освобождает.

Это не очень хорошая идея,

потому что, хотя

сборщика мусора (§5.3), не

гарантируется его

С++ и определяет

интерфейс

наличие для того,

чтобы сде­

лать

неиспользуемую

память

доступной

для

новых

объектов.

Во

многих

сре­

дах

вы

не можете

использовать

сборку

мусора,

и

часто

по

логическим

при­

чинам

или

из

соображений

производительности

предпочтительнее

использо­

вать

более

точное

управление

уничтожением

объектов.

Нам

нужен

механизм,

освобождающий

память,

выделенную

конструктором;

таким

механизмом

яв­

ляется

деструктор:

4.2.

Конкретные типы

class

Vector

puЫic:

/ /

Конструктор: захват

ресурсов:

 

 

Vector(int s)

:elem{new

douЬle[s] },

sz{s}

{

 

 

 

 

 

 

 

 

 

for

(int

i=O; i!=s;

 

++i)

//

Инициализация

 

 

elem[i]=O;

 

 

 

 

 

элементов

11

Деструктор: освобождение

ресурсов

-Vector ()

 

 

{

 

 

 

 

delete[J

elem;

 

81

douЫe&

operator[] (int i);

 

 

 

int size() const;

 

 

 

private:

 

 

 

 

// elem

указывает на массив

из

sz элементов

типа dоиЫе:

douЫe*

elem;

 

 

 

int sz;

 

 

 

 

};

 

 

 

 

Имя деструктора состоит из оператора дополнения - , за которым

следует

имя

класса;

деструктор

-

это

дополнение

к

конструктору.

Конструктор

Vector

выделяет

некоторую

память

в

свободной

памяти

(именуемой

также

кучей или

динамической

памятью)

с

использованием

оператора

new [ ] .

Деструктор

ос­

вобождает

эту

память

с

помощью

оператора

delete

[].

Обычный

оператор

delete удаляет отдельный объект, delete [] удаляет

массив.

Все это делается без вмешательства пользователей

класса

Vector.

Поль­

зователи

просто создают и

используют

объекты

Vector

так

же,

как

и

пере­

менные

встроенных

типов.

Например:

void

fct(int n)

 

 

 

 

{

 

 

 

 

 

 

 

 

Vector

v(n);

 

 

 

 

/ /

 

Использование

v ...

 

 

{

 

 

 

 

 

 

 

 

Vector

v2(2*n);

 

 

 

 

 

/ /

. . .

Использование

v

и

 

}

//

Здесь уничтожается

 

v2

 

/ / . . .

Использование

v ..

 

 

 

11

Здесь уничтожается

v

 

 

v2

...

Vector

подчиняется

тем

же

правилам

именования,

области

видимости,

вы­

деления

памяти,

времени

жизни

и

так

далее

1.5),

что

и

встроенные

типы,

такие как int

и char. Этот Vector представляет собой

вариант из-за

отсутствия обработки ошибок; см. §3.5.

сильно

упрощенный

82

Глава

4.

Классы

Комбинация

конструктора/деструктора

является

основой

многих

элегант­

ных

технологий.

В

частности,

она

является

основой

большинства

методов

управления

ресурсами

в

С++

(§5.3, §13.2).

Рассмотрим

графическую иллю-

страцию

Vector.

Vector: elem: sz:

О: о

1: о

2: о

3: о

4: о

5: о

Конструктор

выделяет

память

для элементов

и

соответствующим

образом

инициализирует

элементы

Vector.

Деструктор

освобождает

память,

вы­

деленную

для

элементов.

Эта

модель

управ.~е11ия

данными

очень

часто

ис­

пользуется

для

управления

данными,

которые

могут

изменяться

в

размерах

в

процессе

жизненного

цикла

объекта.

Методика

получения ресурсов

в

кон­

структоре

и

освобождение

их

в

деструкторе

известна

как

идиома

"Захват

ре­

сурсов

есть

инициализация"

(Resource Acquisition

Is

lnitialization

-

RAll)

и

позволяет

избежать

операций

голым

new",

т.е.

избежать

выделения

памяти

в

общем

коде

и

сохранить

его

только

в

реализациях

хорошо

ведущих

себя

аб­

стракций.

Аналогично

следует

избегать

"голых операций

delete".

Все

это

делает

код

намного

менее

подверженным

ошибкам

и

позволяет

намного

про­

ще

избежать

утечек

ресурсов(§

13.2).

4.2.3.

Инициализация

контейнеров

Контейнер

существует

для

хранения

элементов,

поэтому

очевидно,

что

нам

нужны

удобные

способы

добавления

элементов

в

контейнер.

Мы

можем

создать

Vector

с

соответствующим

количеством

элементов,

а

затем

выпол­

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

другие

способы

более

элегантны.

Здесь

я

Конструктор со

списком

инициштзации: инициализация с помощью

списка элементов.

 

 

 

 

push_back () : добавление

новых элементов

в конец последователь­

ности.

 

 

 

 

Они

могут

быть

объявлены

следующим

образом:

class

Vector

puЫic:

 

 

 

11

Инициализация

списком элементов

типа

Vector(std::initializer_list<douЫe>);

douЫe:

11

...

4.2.

Конкретные

типы

83

11 Добавление

элемента

в

void push_back(douЬle);

 

11 ...

 

 

};

конец

с

увеличением

размера

на

1:

Функция push_back

()

элементов. Например:

 

Vector read(istream& is)

полезна

для

добавления

произвольного

количества

Vector v;

 

 

for(douЫe

d;

is>>d;

v.push_back(d);

return v;

 

 

// 11

Чтение значений

с плавающей

Добавление d в

вектор v

точкой

в

d

Входной

цикл

завершается

при

достижении

конца

файла

или

при

ошибке

форматирования.

До

тех

пор,

пока

это

не

произойдет,

каждое

прочитанное

число

добавляется

в

Vector,

так

что

в

конце работы

функции

размер

вектора

v

равен

количеству

прочитанных

элементов.

Я

использовал

цикл

for,

а

не

более

удобный

цикл

while,

чтобы

ограничить

область видимости перемен­

ной

d,

ограниченную

циклом.

Предоставление

классу

Vector

конструктора

перемещения,

обеспечивающего

дешевый

возврат

потенциально

огромного

количества

данных

из

функции

read

( ) ,

объясняется

в

§5.2.2:

Vector

v

=

read(cin);

//Копирования

элементов

Vector

нет

Способ

представления

std:

:

vector

для

того,

чтобы

сделать

эффективны­

ми

операцию

push

_

back

()

и

другие

операции,

изменяющие

размер

vector,

представлен в §11.2.

Класс std: : ini tializer_list

используется

для

определения

конструк­

тора

на

основе

списка

инициализации

и

представляет

собой

тип

стандартной

библиотеки,

известный

компилятору:

когда

мы

используем

список

в

фигур­

ных

скобках

( {}),

такой

как

{1,

2,

3,

4 },

компилятор

создает

объект

типа

initializer

list

для

предоставления

его

программе.

Так

что

можно

на­

писать:

Vector

Vector

vl v2

{1, 2, 3, (1.23,

4, 5}; 3.45,

6.7,

8};

// //

vl v2

имеет имеет

5

элементов

4

элемента

Конструктор со списком

инициализации

лен следующим образом:

 

класса

Vector

может

быть

опреде­

// Инициализация списком: Vector::Vector(std::initializ

er_list<douЫe>

lst}

:elem{new

douЬle[lst.size()]},

sz{static_cast<int>(lst.size())}

{

Vector

container:

 

4.5. Иерархии классов

89

vtЫ:

 

 

 

~ EEI

Vector_container: :operator[] ()

 

1

1

Vector_container: :siz е()

 

1

1

Vector_ container: : -vector_ container ()

J

List

container:

vtЫ:

-~Н-'

L:J

~i~

List_container: :operator[] ()

_____L_i_s_t c_on_t_a_i_n_e_r_:_:s_i_·z_e_(_)_~

J

List_container::-List_container()

Функции

в vtЫ

позволяют

его размер и

схема

размещения

правильно

использовать

объект, даже

если

его данных

неизвестны

вызывающей

функ­

ции.

Реализация

вызывающей

функции

должна

знать

только

местоположение

указателя

на

vtЫ

в

Container

и

индекс,

используемый

для

каждой

вирту­

альной

функции.

Этот

механизм

виртуального

вызова

можно

сделать

почти

таким

же

эффективным,

как

и

механизм

"нормального

вызова функции"

пределах

25%).

Его

накладные

расходы

в

смысле

памяти

-

один

указа­

тель

в

каждом

объекте

класса

с

виртуальными

функциями

плюс

один

vtЫ

для

каждого

такого

класса.

4.5.

Иерархии

классов

Пример

Container

-очень

простой

пример

иерархии

классов.

Иерархия

классов

представляет

собой

множество

классов,

упорядоченных

в

виде

сетки,

созданной

наследованием

классов

(например,

puЫic).

Мы

используем

ие­

рархии

классов

для

представления

концепций,

которые

имеют

иерархические

отношения,

такие

как

"пожарная

машина

-

это

разновидность

автомобиля,

который,

в

свою

очередь,

является

разновидностью

транспортного

средства",

или

"смайлик

-

разновидность

круга,

который,

в

свою

очередь,

является

раз­

новидностью геометрической

фигуры".

Нередки

огромные

глубокие

и

широ­

кие иерархии с сотнями классов. В качестве полуреалистического

ского примера рассмотрим геометрические фигуры на экране.

классиче­

Shape ~

Circle

Triangle

/

Smiley