Учебное пособие: Препроцессорные средства в C и С++
В программе, использующей объекты классов TIntArray и TRealArray могут создаваться экземпляры этих классов с возможностью обращения к ним через указатель на базовый класс:
TBase *pb; TIntArray aint(5,3);TRealArray areal(4,2);Тогда для печати массивов могут применяться операторы
pb = &aint; pb->print(); //?????? ??????? aint pb = &areal; pb->print(); // ?????? ??????? arealПриведем еще один пример использования виртуальных функций. Пусть некоторый любитель домашних животных решил завести каталог своих любимцев и для каждого вида животных определил свой класс с общим базовым классом Pet. Для краткости ограничимся в описании каждого животного его кличкой и типовым излаваемым животным звуком с возможностью вывода на экран списка кличек и представления издаваемых ими звуков.
Программа:
#include <iostream.h> struct Pet // ???????????? { char *name; virtual void speak() = 0; Pet( char *nm){name=nm;} } struct Dog : public Pet { virtual void speak( ) { cout<<name<<? ??????? ??<<? ?? - ???<<endl; }; Dog(char *nm): Pet(nm) { }; }; struct Cat : public Pet { virtual void speak( ) { cout<<name<<? ??????? ? <<? ???-????<<endl; Cat(char *nm): Pet(nm) { }; } int main () { Pet *mypets[ ] = { new Dog(???????), new Cat(???????),new Dog(?????? ?)}; // ?????? ????????const int sz = sizeof( mypets)/ sizeof( mypets [ 0 ]); for ( int k = 0: k < sz; k++) mypets [ k ]->speak();return 0; }4.6. “Дружественные” (friend) функции
Функция, объявленная в производном классе, может иметь доступ только к защищенным (protected) или общим (public) компонентам базового класса.
Функция, объявленная вне класса, может иметь доступ только к общим (public) компонентам класса и обращаться к ним по имени, уточненному именем объекта или указателя на объект.
Чтобы получить доступ к личным компонентам объектов некоторого класса Х в функции, не имеющей к ним доступа, эта функция должна быть объявлена дружественной в классе X:
class X { friend void Y:: fprv( int, char*);/* ?????? ?????????? ?????? X */ }Можно объявить все функции класса Y дружественными в классе X;
class Y; class X{ friend Y; /* ?????? ?????????? ?????? X */} class Y { void fy1(int, int); int fy2( char*, int);/* ?????? ?????????? ?????? Y */ }Дружественной может быть и функция, не являющаяся компонентой какого-либо класса, например,
class XX { friend int printXX ( );/* ?????? ?????????? ?????? ?? */ }Здесь функция printXX имеет доступ ко всем компонентам класса XX, независимо от закрепленного за ними уровня доступа.
В теории объектно-ориентированного программирования считается, что при хорошо спроектированной системе классов не должно быть необходимости в дружественных функциях, однако в ряде случаев их использование упрощает понимание и последующие модификации программы.
4.7. Статические компоненты класса
Описатель static в С++ имеет различное назначение в зависимости от контекста, в котором он применен.
Переменные и функции, объявленные вне класса и вне тела функции с описателем static, имеют область действия, ограниченную файлом, в котором они объявлены.
Переменные, объявленные как static внутри функции, видимы только внутри этой функции, но сохраняют свои значения после выхода из функции и инициализируются только при первом обращении к функции.
Компоненты класса также могут объявляться с описателем static, такие компоненты - данные являются общими для всех экземпляров объектов этого класса и размещаются в памяти отдельно от данных объектов класса. Доступ к static - компонентам класса возможен по имени, уточненному именем класса (именем типа) или именем объекта этого класса, причем к static - компонентам класса можно обращаться до создания экземпляров объектов этого класса. Статическое данное - член класса должно быть обязательно инициализировано вне описания класса:
class TBase //??????? ????? ??? ???????? ???? ?????{ static int nw; int size, //?????? ???????? count, //??????? ????? ????????? maxCount, //?????? ?????????? ?????? delta; //?????????? ?????? /* ?????? ?????????? ?????? TBase */ } int TBase::nw =1; /* ????????????? ??????????? ?????????? ?????? */Статические компоненты - функции могут вызываться до создания экземпляров объектов этого класса и поэтому имеют доступ только к статическим данным класса:
class X { static int sx1,sx2; static void fsx ( int k); int x1,x2; /* ?????? ?????????? ?????? X */ } int X::sx1 = 1; int X::sx2 = 2; int main () { .......... X:: fsx( 3 ); .............. }4.8. Переопределение (перегрузка) операций
В языках программирования определена семантика операций, выполняемых над базовыми (предопределенными) типами данных, например, если x, y и z - переменные типа float, то запись x = y + z; предполагает интуитивно очевидные действия, сложение x и y и присваивание переменной z полученной суммы.
Желательно было бы и для типов, определяемых в программе, в том числе для классов, определить семантику и алгоритмы операций сложения, вычитания, умножения и т.д., чтобы иметь возможность вместо вызова соответствующих функций записывать просто x + y и в случае, когда x и y являются объектами некоторых классов. В C++ это достигается переопределением имеющихся в языке операций для других типов данных.
Переопределенная операция объявляется так:
???_?????????? operator ????_???????? (?????????? ?????????) { ????????_?????????_??????????_???????? }Например:
class TPoint { int x,y; public: TPoint& operator+=( const TPoint& adder ); TPoint& operator-=( const TPoint& subber ); friend TPoint operator - ( const TPoint& one, const TPoint& two); friend TPoint operator + ( const TPoint& one, const TPoint& two); friend int operator == ( const TPoint& one, const TPoint& two); friend int operator != ( const TPoint& one, const TPoint& two);};Полное определение этих операций для объектов класса TPoint имеет вид:
inline TPoint& TPoint::operator += ( const TPoint& adder ) { x += adder.x; y += adder.y; return *this;} inline TPoint& TPoint::operator -= ( const TPoint& subber ) { x -= subber.x; y -= subber.y; return *this;}Остальные операции определяются аналогичным образом.
Пусть в программе имеются объявления:
TPoint x(12,3), y(21,30), z(18,30);Тогда можно записать:
x +=y; y-=z; TPoint r = x + z:Общие правила переопределения операций сводятся к следующему:
- Двуместные операции должны иметь два параметра, одноместные - один параметр, причем, если операция объявлена как компонента класса, то неявным первым операндом является экземпляр объекта (следовательно при определении двуместной операции будет задаваться один параметр, одноместная операция объявляется с пустым списком параметров). Если операция переопределяется вне класса (с описателем friend ), то для двуместной операции должны быть заданы два параметра, для одноместной операции - один параметр.