Конструкторы.Деструктор

Содержание

Слайд 2

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

Проблема

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

записать
s.len = 0; s.line[0] = ’\0’;
тоже нельзя, т.к. член-данные из части private
недоступны.
( Заметим, что если определить их в части public, то их
можно инициализировать как структуру, т.е.
String s = {“”, 0}; )
Слайд 3

Выход Инициализацию должна выполнять специальная член-функция класса.

Выход

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

Слайд 4

Конструктор! Определение. Член-функция класса, предназначенная для инициализации член-данных класса при определении

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

Определение. Член-функция класса,
предназначенная для инициализации
член-данных класса при определении
объектов класса, называется
конструктором.
Конструктор

всегда имеет имя
класса.
Слайд 5

Конструктор в классе String Объявление : String(); Определение конструктора: String:: String()

Конструктор в классе String

Объявление :
String();
Определение конструктора:
String:: String() { len

= 0; line[0] = ’\0’;} (1)
Определение объектов:
String s1, s2;
.
Слайд 6

Конструктор в классе String Конструктор , и выполняет инициализацию объектов Так

Конструктор в классе String

Конструктор ,
и выполняет инициализацию объектов
Так как конструктор

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

всегда вызывается неявно

Слайд 7

Несколько конструкторов String:: String(const char * s) (2) { for( len

Несколько конструкторов
String:: String(const char * s) (2)
{ for( len = 0;

line[len] != ‘\0’; len++)
line[len] = s[len];
}
Тогда объекты можно определить таким образом
String s1, s2(“Иванов”), s3 = String(“Петров”);
Для объекта s3 конструктор задается явно(но так
конструктор используется редко)

работает конструктор по умолчанию (1)

работает конструктор с аргументом (2)

Слайд 8

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

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

или
несколько с аргументами.
Слайд 9

Особенности конструктора, как функции: 1. Главная - конструктор не имеет возвращаемого

Особенности конструктора, как функции:

1. Главная - конструктор не имеет
возвращаемого значения (даже

void),
так как его единственное назначение –
инициализировать собственные член-
данные объекта;
2. Конструктор имеет имя класса;
3. Конструктор всегда работает неявно
при определении объектов класса
Слайд 10

Недостаток определенного класса String это то, что он берет для каждого

Недостаток определенного класса String

это то, что он берет для каждого объекта

259 байтов памяти, хотя фактически использует меньше
class String{ char *line; int len;
public:
....
};

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

Слайд 11

Другие конструкторы В классе объявим 2 конструктора String(int l = 80);

Другие конструкторы

В классе объявим 2 конструктора
String(int l = 80); //с аргументом

по // умолчанию
String (const char *); //с аргументом // строкой
Слайд 12

String:: String(int l) // l=80 – не повторять! (3) {line =

String:: String(int l) // l=80 – не повторять! (3)
{line =

new char [l]; len=0;
line[0]=’\0’;
}
String::String(const char * s) (2’)
{line = new char [strlen(s)+1];// для нуль-кода
for( len = 0; line[len] != ‘\0’; len++)
line[len] = s[len];
}
Слайд 13

Пример использования String s1(10), s2, s3(“без слов”); конструктор (3) аргумент задан

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

String s1(10), s2, s3(“без слов”);

конструктор (3)
аргумент задан l=10

конструктор (3)
аргумент по

умолчанию l=80

конструктор (2’)
l = 8 + 1 = 9

Слайд 14

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

Замечание

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

(1),
или конструктор с аргументом по умолчанию вида (3)
String ss;
‘Ambiguity between ‘String::String()’ and
‘String::String(int)’ -
‘Двусмысленность между String() и String( int )’
Слайд 15

Инициализация значением другой переменной В С++ кроме инициализации константным значением int

Инициализация значением другой переменной

В С++ кроме инициализации константным
значением
int x =

5;
...
x++;
...
используется и такая инициализация данных
int y = x; // инициализация одного данного // значением другого
Слайд 16

В классе String подобная инициализация может привести к ошибкам. String s(“паровоз”);

В классе String подобная инициализация
может привести к ошибкам.
String s(“паровоз”);
...
String r =

s; // определение объекта r и
// инициализация его значением // объекта s
r.Index(4) = ‘х’ ; r.Index(6) = ‘д’;// изменим на пароход
s.Print(); r.Print();
Увидим, что выведется пароход в обоих
случаях. Это плохо.
Слайд 17

Разберемся, почему это происходит При определении объекта String s(“паровоз”); работает конструктор,

Разберемся, почему это происходит

При определении объекта
String s(“паровоз”);
работает конструктор,

String::String(const char * s)

(2’)
{ line = new char [strlen(s)+1]; // для нуль-кода
for( len = 0; line[len] != ‘\0’; len++)
line[len] = s[len];
}

который возьмет память в динамической области 8 байтов

s:

line

len

и адрес первого запишет в поле s.line.

Затем цикл for занесет в поле *line слово паровоз и
одновременно определит len = 7.

п

а

р

о

в

о

з

\0

7

куча!

Слайд 18

При определении объекта r String r = s; // или String

При определении объекта r
String r = s; // или String r(s);
компилятор

просто выполняет копирование полей
r.line = s.line и r.len = s.len

s:

п

а

р

о

в

о

з

\0

7

r:

7

А значит поле r.line будет
показывать на ту же
динамическую область!

И при выполнении операторов
r.Index(4) = ‘х’ ; r.Index(6) = ‘д’;

х

д

изменятся оба объекта. ☹

line

len

Слайд 19

Что неграмотно и недопустимо !

Что неграмотно и недопустимо !

Слайд 20

Поэтому для инициализации одного объекта другим надо определить специальный конструктор копирования

Поэтому для инициализации одного объекта
другим надо определить специальный
конструктор копирования
X ::

X( X& ); // где X - имя класса
String(String &);
String::String(String &s)
{ line = new char[ s.len + 1 ];
for( len = 0; line[len] != ‘\0’; len++)
line[len] = s.line[len];
}

const

Слайд 21

Тогда инициализация String r = s; выполнится грамотно. s: п а

Тогда инициализация String r = s; выполнится грамотно.

s:

п

а

р

о

в

о

з

\0

7
String::String(String &s)
{ line = new

char[ s.len+1 ];
line = new char[ s.len + 1 ];
for( len = 0; line[len] != ‘\0’; len++)
line[len] = s.line[len];
}

п

а

р

о

в

о

з

\0

7

r:

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

возьмет для объекта r

новую динамическую память длиной s.len + 1 байтов.

И цикл for затем запишет из объекта s в поле r.line слово паровоз, в поле r.len длину 7.

При выполнении операторов
r.Index(4) = ‘х’ ; r.Index(6) = ‘д’;

значение s.line теперь не изменится ! ☺

х

д

line

len

line

len

куча

1

2

3

4

5

6

Слайд 22

Все верно s.Print(); // выведет ‘паровоз’ r.Print(); // выведет ‘пароход’

Все верно

s.Print(); // выведет ‘паровоз’
r.Print(); // выведет ‘пароход’

Слайд 23

Замечание Конструктор копирования кроме рассмотренной инициализации работает также при передаче значений

Замечание

Конструктор копирования кроме
рассмотренной инициализации работает
также
при передаче значений фактических аргументов-объектов в

функцию
при возврате результата-объекта из функции.
Слайд 24

п 3.3. Деструктор В языке С++ одним из важных моментов является

п 3.3. Деструктор

В языке С++ одним из важных моментов
является освобождение памяти,

занятой
переменными, при выходе из функции.
void F()

{ int k;
String s1(20),s2(“ФПМК”),*s3;
s3= new String (“ТГУ”);
}

Слайд 25

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

При выходе из функции освобождается память
для локальных объектов, т.е. k, s1,s2,

s3. Но
рассмотрим внимательнее, как это будет
реализовано.

k:

0

s1:

line

len

\0

...

Пустая строка из 20 байтов

s2:

4

line

len

Ф

П

М

К

\0

4 байта

s3:

new

Т

Г

У

\0

3

line

len

Эта память
будет
“брошена!”

Не экономно!

конструктор 2’

куча

При выходе

Слайд 26

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

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

надо задать
специальную член-функцию
деструктор.
Слайд 27

Деструктор Определение. Деструктор - это член функция класса, предназначенная для освобождения

Деструктор

Определение. Деструктор - это член
функция класса, предназначенная для
освобождения динамической памяти,
занимаемой член-данными

класса, при
выходе из функций. Деструктор имеет
формат
~ имя_класса(){…}
Слайд 28

Пример Для класса String его можно определить таким образом ~ String() {delete [ ] line;}

Пример

Для класса String его можно определить
таким образом
~ String() {delete [ ]

line;}
Слайд 29

Пример В этом случае при выходе из области видимости функции F()

Пример

В этом случае при выходе из области
видимости функции F() память для
объектов

s1, s2, которую брал
конструктор для поля line, будет
освобождена.
Заданный деструктор это будет делать по
умолчанию.
Слайд 30

k: 0 s1: line len \0 ... Пустая строка из 20

k:

0

s1:

line

len

\0

...

Пустая строка из 20 байтов

s2:

4

line

len

Ф

П

М

К

\0

4 байта

s3:

new

Т

Г

У

\0

3

line

len

Работает
деструктор

куча

Работает стандартное

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

Динамическую память, занятую объектом, заданным через указатель s3,

надо освобождать явно операцией
delete s3;

Память по операции
delete s3;
будет освобождена в 3 этапа:

1) деструктором

2) операцией delete

3) Стандартно от ячейки s3
при выходе из функции

Слайд 31

Особенности деструктора как функции: он не имеет аргументов; он не возвращает

Особенности деструктора как функции:

он не имеет аргументов;
он не возвращает значения;
работает неявно

для всех объектов при выходе из функций
Замечание. Работу деструктора можно
“увидеть”, если в деструкторе задать какой-
либо вывод.
~String()
{ printf(“\nРаботает деструктор класса String”);
delete [ ] line; }
Слайд 32

class String {char *line; int len; public: String(int l=80); // конструктор

class String
{char *line; int len;
public:
String(int l=80); // конструктор

по умолчанию
String(const char *); //конструктор с аргументом
String(String &); // конструктор копирования
~String() { delete [] line;} // деструктор
void Print() { cout << line;}
int Len() { return len;}
char & Index( int );
void Fill( const char* );
};
Слайд 33

char & Index (int) char & String:: Index (int i) {

char & Index (int)

char & String:: Index (int i)
{ if(i<0

|| i>=len)
cout << ”\n Индекс за пределами “; exit(0);}
return line[i]; }
Тип возвращаемого значения char & - ссылка
возвращает не просто значение символа, а
ссылку на ячейку, где он находится.
Это и позволяет выполнить присвоение вида
r.Index (4) = ’х’;