Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Шаблоны ( пример template)




Дружественные функции.

Функция, объявленная в производном классе, может иметь доступ только к защищенным (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. Пеpегpузка опеpаций (синтаксис operator)

В языках программирования определена семантика операций, выполняемых над базовыми (предопределенными) типами данных, например, если 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), то для двуместной операции должны быть заданы два параметра, для одноместной операции - один параметр.

- При переопределении сохраняется приоритет исходной операции т.е. операция + будет выполняться раньше операции = и т.д.

- При переопределении не наследуются свойства коммутативности и ассциативности, т.е. результат выражения х + y - z может отличаться от результата выражения y - z + x и зависит от того, как определены соответствующие операции.

- Не допускается переопределение операций. (точка),.* (точка -звездочка, обращение к указателю на компоненту класса или структуры),:: (разрешение контекста), а также операции # и ##, используемые при препроцессорной обработке.

- Переопределяемые операции = (присваивание), () (функция), [ ] (индекс), -> (обращение к компоненте класса по указателю) всегда должны быть компонентами класса и не могут быть static.

- Переопределяемые операции new и delete должны быть static - компонентами класса.

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


5. Статические компоненты класса.

Описатель 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);

..............

}

Пространства имен.


7. Иерархия классов (последовательность работы конструкторов и деструкторов)

Один класс может быть базовым для нескольких производных, производный класс может быть, в свою очередь, базовым для какого-либо класса и т.д. Если производный класс имеет несколько базовых, то такое наследование называется множественным. Принято называть ба-зовый класс родительским, а производный – потомком.

При создании объектов производного класса сначала автоматически вызываются конструкторы базовых классов согласно списку базовых классов в объявлении производного класса, а затем конструктор производного класса. Конструкторы базовых классов вызываются независимо от того, перечислены они явно в конструкторе производного класса или нет. Объекты разрушаются в порядке, обратном их созданию, т.е. вначале вызывается деструктор производного класса, а затем базового.

В базовом и производном классах функции могут иметь одно и то же имя и список параметров, но отличаться телом. (Такие функции называют подмененными.) Для различения функций используется оператор принадлежности.\\\

Объявлена иерархия классов двух простых геометрических объектов: окружности и цилиндра.

Программа составлена так, чтобы внутренние значения переменных г-радиус окружности и h-высота цилиндра определяли параметры создаваемых объектов. Базовый класс Circle моделирует окружность, а производный класс Cylinder моделирует цилиндр.

class SimpleButton: public: TButtonControl { public:

SimpleButton (int x, int y);

void Draw();

-SimpleButton() { });

SimpleButton::SimpleButton(int x, int y):

TButtonControl(x, y) { }

I void SimpleButton::Draw()

I { i outline->Draw();

1)

8. Доступ к наследуемым компонентам

Если доступ к собственным компонентам производных классов определяется обычным образом, то на доступ к наследуемым компонентам влияет, во-первых, атрибут доступа, определенный в базовом классе, и, во–вторых, модификатор доступа (public / private), указанный перед именем базового класса в конструкции определения производного класса.

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

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


9. Виртуальные функции (когда применяются, форма вызова)

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

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

class Based {

virtual void fb(){….}

};

class Derived:public Based {

…..

void fb() {….}

};

Виртуальная функция fb, определенная в производном классе Derived, может отличаться от функции, определенной в базовом классе Based, только телом. Правильный вызов виртуальной функции осуществляется следующим образом. (Классы Derived1 и Derived2 производные от класса Based.)

Based *ptrBase=new Based;

Derived1 der1; Derived2 der2;

ptrBase=&der1;

ptrBase->fb();

ptrBase=&der2;

ptrBase->fb();

Если в базовом классе виртуальная функция равна 0, то такой класс называется абстрактным; объект такого класса создать нельзя (только указатель на объект).

Шаблоны (пример template)

* Шаблоны функций.

Часто встречаются функции, реализующие одни и те же действия для аргументов различных типов. Например, сортировка массива по возрастанию его элементов может выполняться одним и тем же методом и для данных типа int и для данных типа double. Различие состоит только в типах параметров и некоторых внутренних переменных.

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

template < class имя_класса >

Заголовок функции

{ /* Тело функции */ }

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

Параметризованное определение функции сортировки массива методом перестановок может быть построено следующим образом:

template <class T >

void sort (T a[ ], int n)

{ T temp;

int sign;

for (int k = 0; k > n; k++)

{ sign = 0;

for (i = 0; i <n - k; i++)

if (a [ i ] > a [ i + 1])

{ temp = a [ i ];

a[ i ] = a[ i + 1 ];

a[ i + 1 ] = temp; sign++;

}

if (sign == 0) break;

}

return;

}

Если в программе будут объявлены массивы

int aint [10];

double afl [20];

и установлены значения элементов этих массивов, то вызов функции

sort (aint, 10);

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

sort (afl, 20)

обеспечит вызов sort для упорядочения массива с элементами типа double

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

В описателе template можно указывать несколько параметров вида class имя_типа, а также параметры базовых типов. Например, функция

template < class T1, class T2 >

void copy (T1 a[ ], T2 b[ ], int n)

{ for (int i = 0; i <n; i++)

a[ i ] = b [ i ];

}

копирует первые n элементов массива b типа T2 в первые n элементов массива a типа T1. Разумеется, программист несет ответственность за то, чтобы такое копирование было возможным.

. Шаблоны классов

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

template <class T >

class описание класса

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

В качестве примера приведем описание класса stack, предназначенного для построения стеков фиксированного максимального размера с элементами произволного типа.

enum BOOLEAN (FALSE, TRUE);

template <class Type >

class stack

{ private:

enum (EMPTY = -1);

Type* s; /* Указатель на массив стека */

int max_len; /* Максимальная длина стека */

int top; /* Индекс элемента в вершине стека */

public:

stack (): max_len (100) /* конструктор без параметров */

{ s = new Type [ 100 ]; top = EMPTY; }

stack (int size): max_len(size) /* Второй конструктор */

{ s = new Type [ size ]; top = EMPTY; }

~stack () { delete [ ] s; } /* Деструктор */

void reset () { top = EMPTY; } /* Очистить стек */

void push (Type c) { s [ ++top ] = c; }

Type pop () { return (s [top—] }

Type top_of () { return (s [top ] }

BOOLEAN empty () { return BOOLEAN (top == EMPTY) }

BOOLEAN full () { return BOOLEAN (top == max_len) }

};

Следует отметить, что в этом примере с целью сокращения исходного текста не предусмотрен контроль выхода за пределы стека в методах push и pop.

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

stack < int > stack_of_int (50); /* Стек на 50 элементов типа int */

stack < myClass > stmc (20); /* Стек на 20 элементов типа myClass */

В приведенном примере все компоненты-функции определены в описании класса. Когда полное определение функции-члена класса задается вне описания класса, оно должно уточняться описателем template. Например, если бы метод top_of был определен вне описания класса, определение имело бы вид:

template < class Type >

Type top_of () { return s [ top ];}

Отметим некоторые специфические черты описаний параметризованных классов.

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

Если в параметризованном классе имеются статические (static) компоненты, то для каждого значения параметра будет использоваться свой экземпляр статической компоненты.

11. Библиотека STL (контейнеры и итераторы)


Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...