{
x=_x;
y=_y;
}
void putinfo(int &xx,int &yy)
{
xx=x;
yy=y;
}
};
Void main()
{
Coord arg;
coord*p=&arg;
arg.get_info(10,5);//Имя объекта. функция-элемент
Int col,row;
p->putinfo(col,row);//Указатель -> функция-элемент
}
Лекция 18
Класс как область действия
Известны области действия: функция, файл, блок, прототип. К ним добавляется класс, т.к. имена всех элементов класса находятся в области класса. Они могут использоваться только членами-функциями (методами) класса и еще в некоторых других случаях (дружественными функциями, дружественными и производными классами).
Использование указателей на функции-элементы (методы)
Синтаксис определения
тип_результата (имя_класса:: *имя_указателя)(параметры);
Чтобы использовать такой указатель, можно применить либо операцию. *указатель, либо à*указатель.
Пример
#include <stdio.h>
class A
{
public:
void f1()
{
puts (“привет!”);
}
void f2()
{
puts (“пока!”);
}
};
void main()
{
void (A::pf)()=&A::f1;
/*указателю pf на функции-элементы класса А присвоили начальный адрес функции f1()*/
A q; //q – объект класса А
A*pA=&q; //указатель рА на класс А получил адрес объекта q
(q.*pf)(); /равносильно вызову q.f1();
pf=&A::f2; //B pf занесен адрес функции f2().
(q.*pf)(); //q.f2();
(pAà*pf)(); /*в указателе рА находится адрес объекта q. В указателе pf – адрес функции f2(). Вызывается f2() с объектом q, т.е. q.f2()*/
}
Указатель this
Обычная (нестатическая) функция – элемент класса, должна «знать», с каким объектом класса она работает. При вызове такой функции в С++ передается скрытый параметр this. Это указатель, в котором находится начальный адрес объекта, вызвавшего функцию, или с которым работает эта функция. Обычно this используется для возврата результата работы функции по указателю (return this)или по ссылке (return *this) на подразумеваемый объект.
Конкретные примеры использования this будут приведены позже.
Специальные функции-элементы
Это некоторые функции – члены класса, от которых зависят способы создания, копирования, преобразования и уничтожения объектов класса. Часто эти функции создаются по умолчанию. Ниже будут рассмотрены некоторые специальные функции-члены: конструктор, конструктор копирования, операция присваивания, функции преобразования, деструктор.
Конструктор
Это функция – элемент с тем же именем, что и имя класса. Она вызывается компилятором всегда, когда создается объект класса. Конструктор ничего не возвращает. Поэтому для него не указывается тип результата. Он не наследуется, не может быть const, volatile, static, virtual.
Если конструктор явно не описан, то создается по умолчанию вида
Имя_класса ()
{
}
Это т.н. пустой конструктор.
Конструктор, как и другие функции, может быть перегружен. В частности, помимо пустого конструктора, вызываемого только при создании объекта, можно также определить конструктор, который при создании объекта инициализирует его элементы – данные. Это можно сделать как в теле конструктора, так и с помощью списка инициализации элементов. Если есть константные (только для чтения) элементы – данные, то можно использовать только список инициализации.
Пример инициализации в теле конструктора
class coord
{
float x, y;
public:
coord (float a, float b)
{
x=a;
y=b;
}
};
void main ()
{
coord w (2.5, 12.3);
}
При создании объекта w класса coord будет вызван конструктор. Его формальные параметры а и b приобретут соответственно значения 2.5 и 12.3.
Выше уже было сказано, что константные элементы – данные можно инициализировать только с помощью списка инициализации. Этот список отделяется двоеточием (:) от заголовка определения конструктора и содержит элементы-данные (и базовые классы), разделенные запятыми.
Пример.
class coord
{
const float x,y;
public:
coord(float a, float b):x(a),y(b)
{
}
};
void main()
{
coord w(2.5, 12.3);
}
Для неконстантных элементов-данных инициализацию можно призводить или в теле конструктора, или с помощью списка инициализации. Однако список инициализации является обязательным также, если элементом данных одного класса служит объект ранее описанного класса и конструктор этого (ранее описанного) класса реализует инициацию элементов данных любым из двух способов.
Это единственный механизм инициализации в данном случае.
Пример
class coord
{
const float x,y;
public:
coord(float a, float b):x(a),y(b)
{
}
};
class map
{
coord w;
public:
map (float_x, float_y):w(_x,_y)
{
}
};
void main ()
{
map q(1Ø,15); //При создании объекта q класса map передаются данные для //инициализации x(a), x(b)
}
Конструктор копии
Это конструктор специального вида.
Он воспринимает в качестве аргумента константную ссылку на объект класса (const тип_класса &) или простую ссылку на объект (тип_класса &).
Заголовки конструктора копииимеют вид
имя_класса (const имя_класса&имя_источника_копирования)
или при простой ссылке
имя_класса (имя_класса&имя_источника_копирования).
Константную ссылку на объект ставят, чтобы избежать случайного присваивания источнику копирования.
Конструктор копии вызывается всякий раз, когда создается новый объект класса и его необходимо инициализировать значениями уже существующего объекта этого же класса (источника копирования).
Конструктор копии может создаваться автоматически (по умолчанию), если он не определен программистом явно при описании класса. Такой конструктор создает буквально копию объекта. Это не всегда приемлемо для объектов, содержащих указатели или ссылки.
Пример
#include <stdio.h>
class point
{
float x,y;
public:
point (float a, float b)
{
x=a;
y=b;
}
point (const point &src)
{
x=src.x;
y=src.y;
}
};
void main()
{
point v(2.43, 8.1);
point w=v;
}
При создании объекта v класса point вызывается конструктор, который при этом одновременно инициализирует элементы-данные. В результате v.x=2.43, v.y=8.1.
При создании объекта w класса point вызывается конструктор копии. Вместо формального параметра src фактическим источником для копирования является созданный объект v. В результате w.x=2.43, w.y=8.1.
ВНИМАНИЕ! Если нужно запретить копирование объектов данного класса, необходимо явно определить конструктор копии и поместить его в область действия метки доступа private. Следует также иметь в виду, что если описан явно хотя бы один из конструкторов, остальные также требуется определить явно. Наличие хотя бы одного явно определенного конструктора исключает автоматическое создание каких-либо других конструкторов.
Операция присваивания
Эта операция является функцией-элементом класса с именем operator=. Воспринимает единственный аргумент типа const тип_класса& или тип_класса&. Имеет два вида:
тип_класса& operator=(const тип_класса &x)
тип_класса& operator=(тип_класса &x).
Как и в конструкторе копии, const применяется в случае желания подстраховаться от ошибочного присваивания какого-либо значения передаваемому в функцию элементу источнику, значение которого должно присваиваться другому объекту.
Операция присваивания вызывается компилятором, когда объекту присваивается значение другого объекта. Это может происходить неоднократно, а не только при создании объекта с инициализацией, когда вызывается конструктор копии. Также в отличие от конструктора копии операция присваивания может реализовать цепочку присваиваний, например:
x=y=b;
Если операция присваивания явно не определена, компилятор генерирует его по умолчанию. В этом случае реализуется побитовое присваивание. Это не всегда приемлемо для объектов, содержащих указатели или ссылки.
Рассмотрим пример явного определения операции присваивания на примере класса комплексных чисел.
Пример
class comp
{
float Re, Im;
public:
comp (float a, float b):Re(a),Im(b)
{
}
comp& operator=(const comp& src)
{
Re=src.Re;
Im=src.Im;
return *this;
}
comp (const comp& x)
{
Re=x.Re;
Im=x.Im;
};
void main()
{
comp x(1,2), y(-Ø.5,1.25);
comp z=x; //Вызывается конструктор копии
comp v,w; //Вызывается «пустой» конструктор
v=w=y; //Вызывается operator=()
}
Рассмотрим более детально, как реализуется последовательное присваивание v=w=y;
По знаку присваивания «=» вызывается функция-элемент operator=(). Объект у является фактически параметром, которым инициализируется ссылка
const comp& src в функции operator=().
Адрес объекта w заносится в указатель this функции-элемента операции присваивания. Таким образом, после окончания присваивания по return*this будут получены значения w.Re=- Ø.5; w.Im=1.25.
После этого снова вызывается operator=(). В качестве фактического параметра передается w, а адрес объекта v заносится в указатель this и т. д.
Именно return*this, возвращающий ссылку, отличает operator=() от конструктора копии.
ВНИМАНИЕ! Если необходимо закрепить присваивание, нужно явно определить operator=() и поместить его в область действия метки доступа private.
Деструктор
Это дополнение к конструктору. Он имеет то же имя, что и класс, но с префиксом-тильдой (~). Вызывается всякий раз, когда уничтожается объект данного класса. Заголовок деструктора имеет вид
~имя_класса()
Для деструктора существуют следующие правила:
1) не может иметь аргументов;
2) не может возвращать значения;
3) не наследуется;
4) не может быть объявлен как const, volatile, static.
Если деструктор явно не определен, он создается компилятором по умолчанию.
Деструктор вызывается по умолчанию:
1) при выходе из области видимости;
2) когда возвращаемые функцией значения объектов данного класса больше не нужны;
3) при выполнении операции delete для объектов, размещенных в динамической памяти.
Если нужно разрушить отдельный объект, можно вызвать деструктор явно имя_объекта. ~ имя_класса ();
Например, s. ~ tree (); Разрушен объект s класса tree.
this – > ~ tree (); Явное разрушение объекта класса tree, адрес которого находится в указателе tris.
Функции преобразования
Для встроенных типов (int, long, char и др.) имеется возможность явного преобразования типа. Необходимость такого преобразования может возникать и для объектов того или иного класса. Объекты класса могут быть преобразованы к другому типу или с помощью конструкторов преобразования, или специальной операции приведения.