Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабник по C.pdf
Скачиваний:
78
Добавлен:
01.06.2015
Размер:
876.9 Кб
Скачать

– 43 –

Например, для класса Complex можно определить функции getR и getI как дружественные:

friend double getR( Сomplex a){ return a.re;} friend double getI( Сomplex a){ return a.im;}

Обращение к ним следующее: cin>>getR(m2); cin>>getI(m2);

при необходимости использовать методы одного класса для обработки закрытых дан-

ных другого класса. Объявление функции производится по следующей схеме: class Y {

...

void fy (){...} //Определение функции класса Y

...

};

class X {

...

void fx(){...}

friend void Y::fy();// Объявление функции fy дружественной классу X

...

};

Пример

#include <iostream.h>

class X; // Объявление класса Х class Y {

int a; public:

Y(int n):a(n){}; // Форма записи аналогична {a=n;} void display(X* pX); //Функция-компонент класса Y

}; class X{

int a; public:

X(int n):a(n){};

friend void Y::display(X* pX); .// Функция объявляется дружественной классу Х и

}; // имеет доступ к полю а как класса Х, так и класса

Y

void Y::display(X* pX){ cout<<pX->a<<'\t'<<a<<endl;

}

void main(){ X mx(100); Y my(200);

my.display(&mx);

 

}

 

Результат на дисплее: 100

200

Все функции одного класса могут быть дружественными другому классу.

12.5. ПЕPЕГPУЖЕННЫЕ ОПЕPАЦИИ И ФУНКЦИИ

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

тип operator знак_опеpации(типы аpгументов){... }.

© 1998 Калачев Д.П., Лутай В.Н.

– 44 –

Перегруженная операция может быть определена как компонент класса; в этом случае она имеет один параметр или вообще не имеет параметров. У дружественной перегруженной операции может быть один или два параметра. Поэтому бинарные операции следует перегружать как дружественные.

Пpимеp для класса Сomplex: class Complex {

double re,im; public:

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

friend Complex operator+(Complex, Complex); };

Complex operator+( Complex l, Complex m) { return Complex (l.re+m.re, l.im + m.im);}

main(){

Complex m1(5,2),m2(6,6),m3(0); m3=m1+m2;

return 0;

}

Операция ’+’ вне класса Complex действует как обычно.

(Функция operator+(a,b) позволяет использовать краткую форму записи a+b. Мы могли бы без перегрузки записать m3=operator+(m1,m2); это относится и к другим операциям)

Перегружаться могут практически все операции (исключение – оператор принадлежности ::, операторы . .* ?: sizeof и символ #). При этом функция operator не изменяет приоритеты операций и число операндов, определенных в языке.

Функция operator для переопределения бинарных операций может быть описана как функция-компонент класса с одним аргументом (например, для операции сложения a.operator+(b) ) или как дружественная функция с двумя аргументами operator+(a,b) ); унарная операция как функция-компонент без аргументов или как дружественная функция с одним аргументом. Четыре функции operator=, operator[], operator() и operator-> должны быть нестатическими компонентами-функциями класса.

Все перегруженные операции, кроме ’=’ наследуются. Относительно последней нужно отметить, что по умолчанию при ее использовании выполняется почленное копирование одного объекта в другой; например, если для класса String запишем:

String s1(10), s2(10); s1=s2;

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

X&x:: operator=(X&)

Для класса String можно записать: void String::operator=(String& a) {

if (this==&a) return; //для предотвращения s1=s1; delete charPtr; //чистка памяти

length=a.length;

charPtr= new char[length+1]; strcpy(charPtr,a.charPtr);

}

Перегруженные функции имеют тот же смысл, что и операции: функция с одним и тем же именем работает по-pазному с различными типами аргументов. Примером является набор конструкторов в классе String.

Функции new и delete перегружаются c помощью функции operator следующим образом:

void* operator new(size_t t [,список аргументов]),

где size_t – первый и единственный обязательный аргумент.

© 1998 Калачев Д.П., Лутай В.Н.

– 45 –

Например, перегрузка

void* operator new(size_t t, int n){ return new char[t*n]; }

позволяет добавить к стандартной функции new еще один аргумент – количество блоков памяти размером size_t каждый. Предположим, что функция вызывается с n=5 и t=double: double *d=new(5) double;

Значение t в теле функции станет равным sizeof(double)=8 (для персональных компьютеров); в результате под переменную будет выделено 5*8=40 байтов памяти.

12.6. ПАРАМЕТРИЗИРОВАННЫЕ КЛАССЫ

Параметризированный класс представляет собой некоторый шаблон (template), определяющий правила построения отдельного класса из множества разрешенных. Рассмотрим пример шаблона для построения стека (Stack) элементов типа Type:

template <class Type> class Stack{

Type *top, *sp; // указатели на начало и текущее состояние стека int size; // размер стека

public:

Stack(int n) {top=sp=new Type[size=n]; } ~Stack() {delete[] top; }

//Операция += перегружается как операция ”заталкивания” в стек;

//cerr - стандартный поток для вывода сообщений об ошибках на дисплей void operator+=(Type v) {

if (size>sp-top) *sp++=v; else cerr<<”Error\n”; }

//Операция --перегружается как операция извлечения из стека

Type operator--() {

if (sp==top){ cerr<<”Error:\t”; return *sp; } return *--sp; }

};

Имея шаблон, можно объявить стек с любым типом элементов: void main () {

Stack <int>si(2); // Объявление стека целых размером 2 si+=100; // Целое заталкивается в стек

si +=234;

si+=567; // Ошибка-результат Error cout<<--si<<endl; // Извлекается 234 cout<<--si<<endl; // Извлекается 100 cout<<--si<<endl; // Ошибка - результат Error:100

}

Здесь методами класса являются встроенные функции. Если бы их определяли вне класса, то следовало бы их записать как параметризированные функции:

template <class Type> Type Stack<Type>:: operator--() { if (sp==top){ cerr<<“Error:\t”; return *sp; }

return *--sp; }

template <class Type> void Stack<Type>:: operator+=(Type v) { if (size>sp-top) *sp++=v; else cerr<<“Error\n”; } )

Параметром шаблона, который передается в конструируемый класс, является Type. Его значением может быть не только встроенный, но и введенный пользователем тип. Для того, чтобы построить, например, стек комплексных чисел, надо описать в программе соответствующий класс, а затем объявить Stack<Complex>ss(3);

К параметризированным классам примыкают контейнеры классов. Контейнер – множество классов, которые отличаются только типами используемых в них данных; контейнеры содержат типовые структуры и типовые операции над данными, входящими в эти структуры. В библиотеке системы Borland C++ содержатся контейнеры, которые включают бинарные деревья, кэш-таблицы, списки, векторы, массивы, очереди, словари, множества и стеки.

© 1998 Калачев Д.П., Лутай В.Н.

– 46 –

Из этих структур можно конструировать классы с различными типами данных. Ниже приведена программа построения массива объектов типа double c использованием класса TSArrayAsVector, для которого задан шаблон в библиотеке системы.

#include <iostream.h> #include <classlib\arrays.h> void main(void){

TSArrayAsVector<double>a(10); // a - контейнер объектов типа double, //10 - число объектов в контейнере;объекты в контейнере отсортированы

double j;

for(int i=0; i<10; i++){ cin>>j;

a.Add(j); } // добавление объектов в контейнер for (i=0; i<10; i++)

cout<<a[i]<<'\t';

}

В общем случае конструктор TSArrayAsVector имеет следующий вид:

TSArrayAsVector(int upper, int lower=0,int delta=0),

где upper – верхняя граница индекса, lower - нижняя его граница , delta – приращение числа объектов. Если в предыдущем примере записать ненулевое приращение, например, объявить a(10,0,10), то после введения 10-го объекта в массив автоматически добавится еще 10 полей для новых объектов. Это позволяет решать проблему расширения массивов и не требует оценки размера массива на этапе проектирования программы.

12.7. ПРЕОБРАЗОВАНИЕ ТИПОВ

Конструктор класса, вызванный при создании объектов, выполняет преобразование переменных: базовый_тип->класс. Для обратного преобразования из заданного класса в базовый тип следует определить преобразование типа, добавив в описание класса функцию operator:

class Myclass { double d;

. . .

public:

Myclass(double x) : d(x){ } operator double() {return d; }

};

void main() { Myclass mc1(10.83); double mc2; mc2=mc1;

}

Аргументом оператора return может быть выражение (*this) .

12.8. СТАТИЧЕСКИЕ КОМПОНЕНТЫ КЛАССА

Компоненты класса как поля, так и функции можно объявлять статическими (static). Эти элементы занимают статическую область памяти и являются общими для всех объектов класса.

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

ведем пример:

void f() {cout<<“Я - глобальная функция”<<endl;} //Глобальная функция class MyClass {

static int a; public:

© 1998 Калачев Д.П., Лутай В.Н.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]