WWW.DISSERS.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА

   Добро пожаловать!


Pages:     | 1 |   ...   | 6 | 7 || 9 | 10 |   ...   | 17 |

void f( ){ cl obj(10); // Объект создан и инициализирован! obj.show( ); // Здесь объект obj используется! } Возможна другая, полная форма записи объявления объекта абстрактного типа, имеющего конструктор:

cl obj = cl (10);

В этом примере конструктор – так называемая инлайн-функция (inline), так как его определение находится в теле класса. Однако его можно представить и как обычную функцию, для чего в классе конструктор только объявляется, а определяется он вне тела класса с использованием квалифицированного имени:

class cl{ int num ;

public:

cl ( int i );

void show( ){cout <<“Число:” << num <<‘\n‘;} };

cl::cl( int i ) { // Полное, или квалифицированное имя.

num = i;} Часто бывает удобно предусмотреть несколько путей инициализации, используя механизм переопределения функций.

Приведем пример программы, в которой происходит вывод строки символов на экран.

# include < conio.h > # include< stdlib.h > # include< string.h > class string { char *str;

unsigned char attr;

int row, col;

public:

string ( );

string (char *, unsigned char, int = 0, int = 0 );

void write ( );

};

// Конструктор без аргументов: определяются все данные объекта - // строка, видеоатрибут ее символов и позиция для вывода на экран.

string::string ( ) { str = new char [ sizeof “Здравствуйте !” ];

strcpy ( str, “Здравствуйте !” );

attr = BLUE << 4 +YELLOW; // Символ – желтый на синем фоне.

row=15;

col=36;

} string::string ( char *line, unsigned a, int y, int x){ str = new char [ strlen (line) +1];

strcpy (str, line);

attr = a;

row = y;

col = x;

} void string::write() { textattr( attr ); // Стандартная функция установления видеоатрибута.

gotoxy( col, row );

cputs ( str );

} void main( ){ string string1; // Эквивалентно string string1=string ( );

// Написать string string ( ); нельзя, так как это – прототип функции! string string2 (“Строка вторая!“, BLACK<<4+WHITE);

string string3 (“Третья строка! “, BROWN<<4+GRAY, 17, 19);

// Печать строк:

string1.write ( );

string2.write ( );

string3.write ( );

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

(В данном случае объект string1 инициализирован конструктором без аргументов и при вызове функции string.write( ) произойдет печать строки Здравствуйте! желтого цвета на синем фоне в 15-й строке начиная с 36-й позиции.) Объекты string2 и string3 инициализируются другим конструктором.

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

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

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

Это удобно выполнить в деструкторе – функции, которая вызывается для объекта абстрактного типа, когда он выходит из области существования. В рассмотренном выше примере место для хранения строк в памяти выделяется динамически, поэтому полезно определить деструктор. Имя деструктора, как и конструктора, не может быть произвольным, оно образуется символом ~ и именем класса (дополнение к конструктору):

class string {...

public:

~ string ( ) { delete str; }...

};

Здесь деструктор очень прост. Он может быть сложнее, и оформлен в виде аутлайн-функции. Деструктор никогда не может иметь аргументов. Отметим, что нельзя получить адрес ни конструктора, ни деструктора. Вызов конструктора происходит автоматически во время определения объекта абстрактного типа. Вызов деструктора происходит автоматически при выходе объекта из области своего существования. Деструктор может быть вызван и явно с обязательным указанием его полного имени. Отметим также, что для класса, в котором явно не определен ни один конструктор, компилятор самостоятельно генерирует так называемый конструктор по умолчанию, не имеющий аргументов, с уровнем доступа public. То же относится и к деструктору.

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

18.4. Статические члены класса Данное-член класса можно объявить со служебным словом static.

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

Если статический член имеет доступ public, то его также можно использовать в программе с помощью квалифицированного имени, как и обычным образом, с помощью уточненного имени.



Пример:

Напишем класс object, в статическом члене которого хранится число существующих в каждый момент времени объектов типа object.

class object { char *str;

public:

static int num_obj;

object ( char *s){ // Конструктор.

str = new char [strlen (s) + 1];

strcpy ( str, s );

cout <<“Создается ” << str <<‘\n’; num_obj ++ ;

} ~ object ( ){ cout <<“Уничтожается ” <

delete str;

num_obj – –;

} };

int object::num_obj = 0; // Инициализация. Об этом говорит // ключевое слово int! object s1 (“первый глобальный объект.”, s2 (“второй глобальный объект.”);

void f ( char *str ) { object s ( str );

cout <<“Всего объектов – ” << object::num_object<<“.\n“;

cout <<“Проработала функция f()” <<“.\n“;} void main ( ) { cout <<“Пока объектов – “ <

object m (“объект в main ( ).”);

cout <<“А сейчас объектов – ” << m.num_obj <<“.\n“;

f (“локальный объект.”);

f (“другой локальный объект.”);

cout <<“Перед окончанием main () объектов – ” <

} Результаты работы программы:

Создается первый глобальный объект.

Создается второй глобальный объект.

Пока объектов – 2.

Создается объект в main ( ).

А сейчас объектов – 3.

Создается локальный объект.

Всего объектов – 4.

Проработала функция f().

Уничтожается локальный объект.

Создается другой локальный объект.

Всего объектов – 4.

Проработала функция f().

Уничтожается другой локальный объект.

Перед окончанием main ( ) объектов – 3.

Уничтожается объект в main ( ).

Уничтожается второй глобальный объект.

Уничтожается первый глобальный объект.

Обратим внимание, что конструкторы для глобальных объектов вызываются до функции main (), а деструкторы после main( ).

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

18.5. Указатель this Рассмотрим пример:

class str { char * string;

public:

void set( char *text){string = text;} void write () { cout<<”Строка: ”<

void main(){ str str1, str2;

str1.set (“Привет!”);

str2.set (“Hello!”);

str1.write ();

str2.write ();

} В результате выполнения этой программы на экране появится следующее:

Строка: Привет! Строка: Hello! Зададимся вопросом: как функция-член write узнает, для какого именно объекта она вызвана Функция-член определяет, для какого объекта она вызвана потому, что ей в качестве неявного первого аргумента передается адрес этого объекта. В данном случае – указатель типа str*.

Внутри функции-члена класса можно явно использовать этот указатель. Он всегда имеет имя this (ключевое слово).

Перед началом выполнения кода функции указатель this инициализируется значением адреса объекта, для которого вызвана данная функция-член. Таким образом, приведенное выше определение функции str::write() представляет собой сокращенную форму следующей записи:

void write(){ cout <<”Строка :”<string<<’\n’;

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

18.6. Статические функции-члены Перед объявлением функции-члена класса можно поставить служебное слово static. Особенностью таких статических функций-членов является следующее: как и к статическому данному-члену класса, к ней можно обратиться еще до того, как в программе создан первый объект такого класса. Статические функции-члены (компонентные функции) позволяют получить доступ к частным статическим данным-членам класса, не имея еще ни одного объекта данного типа в программе.

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

Пример:

class prim{ int numb;

static stat;

public:

prim (int i) { numb=i;

} /* Далее – статическая функция. Указатель this не определен и выбор объекта осуществляется по явно переданному указателю.

Член stat не требует указателя на объект, так как он общий для всех объектов класса prim.

*/ static void func (int i, prim *p = 0) { if (p) p-> numb = i;

else stat = i;

} static void show ( ){ /* Статическая функция обращается только к статическому члену класса, никаких указателей не требуется: */ cout<<”stat=”<

} }; // Конец класса prim.

int prim::stat = 8; // Инициализация статического члена класса.

void main(){ /* До создания объектов типа prim возможен единственный способ обращения к статической функции-члену: */ pкim::show ();

// Можно изменить значение статического члена класса:

prim::func(10);

/* После создания объекта типа prim можно обратиться к статической функции обычным для абстрактных типов способом: */ prim obj(23); // obj.numb становится равным 23.

obj.show();

// Можно изменить значение созданного объекта:

prim::func(20, &obj); // obj.numb = = 20.

obj.func(27, &obj); // obj.numb = = 27.

} 18.7. Указатели на члены класса Для членов класса (кроме битовых полей) определена операция получения адреса. Указатели на данные-члены класса никаких особенностей не имеют. Особенностью указателя на функцию-член класса является явное присутствие в его объявлении имени класса, за которым следует ::.





class cl {...

public:

int f (char*, int);

void g();

...

};

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

int (cl ::*fptr) (char *, int) = cl::f;

Пример:

struct s {int mem;

s (int a){mem=a;} void func(int a){cout<

void main(){ void (s::*fp)(int) = s::func;

s obj(5);

s *p = &obj;

// Два варианта вызова функции-члена по указателю – (obj.*fp)(6); // используя объект obj типа s (p->*fp)(9); // и указатель p на него.

} Здесь.* (как и ->*) являются символами одной – единственной операци, а не находящимися рядом символами двух ранее знакомых нам операций ‘.’ (‘->’) и *. Правым операндом операций.* и ->* обязательно должен быть указатель на член класса, а не любой указатель.

18.8. Инициализация данных–членов класса Инициализация членов абстрактных типов Пусть класс содержит в себе члены абстрактных типов. Особенностью их инициализации является то, что она выполняется с помощью соответствующего конструктора. Рассмотрим класс class coord {double x, y, z;

public:

coord (){x = y = z =0;} coord (double xv, double yv, double zv=0) { x = xv; y = yv; z = zv;} coord (coord & c){x = c.x; y = c.y; z = c.z;} };

class triang{ coord vert1, vert2, vert3; // Координаты вершин треугольника.

public:

triang ();

triang (coord &v1, coord &v2, coord &v3);

};

При инициализации некоторого объекта класса triang потребуется три раза вызвать конструкторы для его вершин – объектов типа coord. Для этого в определении конструктора класса triang после двоеточия нужно поместить список обращений к конструкторам класса coord:

Traing::triang (coord &v1, coord &v2, coord &v3):

vert1 (v1), vert2 (v2), vert3 (v3){...} Вызов конструкторов класса coord происходит до выполнения тела самого конструктора класса triang. Порядок их вызова определяется порядком появления объявлений членов типа coord при создании класса triang.

Класс coord содержит конструктор без аргументов. Вместо записи при обращении к такому конструктору triang::triang (): vert1(), vert2(), vert3() {...} допускается написать просто так:

triang::triang (){...} Инициализация констант Если среди данных-членов класса имеются члены, описанные с модификатором const, то при инициализации используется та же форма записи конструктора, что и в случае с данными абстрактных типов:

class cl{ int v;

const c;

public:

cl (int a, int b):c (b) {v=a;} };

Константу можно инициализировать только в конструкторе, попытка сделать это любым другим способом (например, с помощью другой компонентной функции) приведет к сообщению об ошибке. Инициализация констант в теле конструктора тоже недопустима.

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

class ro{ int var; const c;

public:

ro (int v, int u): c(u), var (v) {} };

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

1) Вновь создаваемый объект должен стать копией уже имеющегося.

2) Нужно скопировать один объект в другой, когда оба были созданы ранее.

В первом случае используется конструктор копирования, во втором – операция присваивания.

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

сlass cl{...

cl (cl&); // Конструктор копирования.

...

};

cl ca; // Здесь используется конструктор без аргументов.

cl cb = ca; // Используется конструктор копирования.

Инициализация копированием происходит и при передаче функциям их аргументов и при возврате результата. Если аргумент или возвращаемое значение имеет абстрактный тип, то неявно вызывается конструктор копирования, как это было в примере с классами coord и triang.

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

Пример1:

class cl {int num; float val;

public:

cl (int i, float x) {num=i; val=x;} };

void main () {cl obj1 (10, 20.3);

// Для создания объектов obj2 и obj// используется конструктор копирования по умолчанию:

cl obj2 (obj1);

cl obj3 = obj2;

} Пример2:

class prim{int n; float v;

public:

prim (int i, float x){n=i; v=x;} prim (const prim &obj, int i = 0){ if (i) n=i;

else n=obj.n;

v=obj.v; } };

void main () { prim obj1(10, 23.5);

/* Для создания объектов obj2 и obj3 используется явно описанный конструктор копирования: */ prim obj2 = obj1;

prim obj3 (obj1, 12);

} Теперь внесем минимальные изменения, и компилятор будет вынужден в дополнение к имеющемуся конструктору копирования добавить свой:

class prim {int n; float v;

public:

prim (int i, float x){n=i; v=x;} prim (const prim &obj, int i){ n = i; v = obj.v; } };

void main() { prim obj1(10, 23.5);

Pages:     | 1 |   ...   | 6 | 7 || 9 | 10 |   ...   | 17 |










© 2011 www.dissers.ru - «Бесплатная электронная библиотека»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.