- •Int YearOfTheBirth; //год рождения
- •Int YearOfTheBirth;
- •Void prn () const
- •Int YearOfTheBirth;
- •Void prn () const;
- •Inline void student :: prn() const
- •Int YearOfTheBirth;
- •Void prn () const;
- •Inline void student :: prn() const
- •Пример выполнения работы (структура боксер):
- •Void prn () const
- •Int main()
Int YearOfTheBirth;
char* grupp;
Void prn () const;
};
Inline void student :: prn() const
{ cout<< "ФИО "<< name << endl;
cout<< "Год рождения "<< YearOfTheBirth <<endl;
cout<< "Группа "<< grupp << endl;
};
Здесь :: - операция разрешения области видимости, указывает компилятору, что prn является членом структуры student и этой функции видимы (т.е. могут использоваться непосредственно) поля структуры. Если вы не хотите, чтобы функция была inline, или
функция не может быть inline, то это служебное слово следует исключить.
Важной возможностью структур С++ ( но не языка С) являются перегрузка операций. Перегрузка операций позволяет использовать, например, операции
>,<,==,!=,>=,<=, +,- и другие для нового, созданного вами типа данных, самым естественным образом. Для демонстрации перегрузки операций дополним структуру student членом типа float, содержащим среднее значение всех проставленных к данному моменту в зачетку отметок а также функцией – членом, осуществляющим перегрузку операции > таким образом, что выражение stud1>stud2 (stud1 и stud2 – экземпляры структуры student) будет означать: если средняя отметка 1-го студента выше, то выражение будет равно 1 (в противном случае – 0).Для перегрузки операции создается функция, прототип которой состоит из типа возвращаемого операцией значения (за исключением случая перегрузки операции приведения типа), служебного слова operator, символа (или символов) перегружаемой операции и размещенного, как обычно в круглых скобках, списка описаний аргументов (параметров),нужных для вычисления результата перегружаемой операции:
struct student
{char name[70];
Int YearOfTheBirth;
float mark; //средняя отметка
char* grupp;
Void prn () const;
int operator > (student /*другой студент */)const;
};
Inline void student :: prn() const
{ cout<< "ФИО "<< name << endl;
cout<< "Год рождения "<< YearOfTheBirth <<endl;
cout<< "Средняя отметка "<< mark <<endl;
cout<< "Группа "<< grupp << endl;
};
inline int student :: operator > (student OtherStudent) const
{ if mark > OtherStudent.mark return 1;
return 0; //в противном случае
}
Тело этой функции с помощью операции "?" можно написать проще:
{return mark > OtherStudent.mark ? 1:0;}
Операция "?" подобна функции ЕСЛИ в Excel.
Вызов перегруженной операции приведет к копированию в системный стек достаточно большой структуры OtherStudent, что невыгодно. Лучше сообщить функции адрес этой структуры и тем самым избежать копирования. Наиболее простой способ добиться этого – применить тип данных ссылка (в С++ ссылка и указатель – разные типы данных) Прототип функции, перегружающей операцию > будет выглядеть так
int operator > (student &)const;
или
int operator > (student & OtherStudent) const;
что равноценно. Символ & (здесь: ссылка на тип данных student) означает передачу структуры OtherStudent в функцию по адресу , без копирования, – подобно var в аналогичной ситуации в Паскале. Передача параметров в функцию по ссылке или по указателю применяется также для того, чтобы функция могла изменить их значения. Чтобы зрительно отличить эти два случая, применяют служебное слово const : int operator > (const student & OtherStudent) const;
которое запрещает изменять параметр в теле функции. C++ несколько злоупотребляет символом & . Он может обозначать ссылку, операцию получения адреса , побитовую логическую операцию И , но значение этого символа всегда ясно из контекста. Нельзя перегрузить операцию (т.е. придать ей другой смысл) для базовых типов языка. Для перегружаемых операций можно использовать только те обозначения операций, которые уже есть в языке (т.е, +, *, <<,&,|, new, / , [] и т.д.), ибо только для них компилятору известны приоритет и направление выполнения (слева направо или наоборот).
Важным преимуществом языка С++ является возможность применения
шаблонов в функциях и структурах. Шаблоны дают возможность создать одно и то же описание функции и структуры для в определенной степени произвольного типа аргумента (в случае функции) или поля (в случае структуры, класса). При создании экземпляра структуры с помощью специального синтаксиса шаблон заменяется конкретным типом поля.
Для примера создадим структуру, описывающую комплексное число. С++ 3.1 фирмы Borland имеет класс complex, поля данных которого имеют тип double и его размер равен таким образом 16 –ти байтам. Класс complex совершенно необходим, например, для вычисления спектрограммы пряжи по результатам измерения ее линейной плотности, которое (вычисление) должно использовать метод быстрого преобразования Фурье (БФП). Т.к. данные измерения имеют в лучшем случае тип integer (а иногда и char -256 уровней), а замеры следуют через 1-2 мм, то применение класса complex приведет к неоправданному расходу памяти и увеличению времени вычисления. Использование шаблона делает описание структуры complex пригодным как для изложенного случая(т.е члены complex смогут иметь любой числовой тип данных) , так и для вычислений с повышенной точностью:
/*1*/template <class T> //шаблон, T – произвольный идентификатор
struct complex
{T re, im;};//действительная и мнимая части имеют неизвестный заранее тип
/*2*/ complex() { re=im = T();} //конструктор,обнуляюoий поля
/*3*/ complex(T x, T y=0) { re=x;im =y;} //перегруженный конструктор
/*4*/ complex operator + (){return *this;} //вернуть самого себя
/*5*/ complex operator - () {return complex<T>(-re, -im);}
//перегрузка оператора + для сложения комплексных чисел
/*6*/ complex operator+( complex & cmpl2)
{ return complex<T>(re + cmpl2.re, im + cmpl2.im);}
//перегрузка оператора - для вычитания комплексных чисел
/*7*/ complex operator-( complex & cmpl2)
{ return complex<T>(re - cmpl2.re, im - cmpl2.im);}
//перегрузка оператора * для умножения комплексных чисел
/*8*/ complex operator*( complex & cmpl2)
{ return complex<T>(re * cmpl2.re - im * cmpl2.im,
re * cmpl2.im + im * cmpl2.re );
}
//перегрузка оператора / для деления комплексных чисел -прототип
/*9*/ complex operator / ( complex<T> & cmpl2);
void prn () const {cout << '{'<< re<<" , "<<im <<'}';}
};//конец структуры
//тело функции для деления комплексных чисел – описание вне тела структуры
/*10*/ template <class T>
inline complex<T> complex<T>:: operator / ( complex<T> & cmpl2)
{ T tmp =cmpl2.re*cmpl2.re + cmpl2.im*cmpl2.im;
T real = (re* cmpl2.re + im* cmpl2.im )/tmp;
T imped =(im* cmpl2.re - re* cmpl2.im )/tmp;
return complex<T> (real,imped); //вызов конструктора
}
Шаблон структуры вводится служебным словом template после которого в угловых скобках располагается список шаблонов. Элементы списка разделяются запятыми. Каждый элемент состоит из служебного слова class ( не имеет ничего общего с классом – типом, в более поздних версиях оно может быть заменено более подходящим к данному случаю служебным словом typename) и следующего за ним идентификатора шаблона. Идентификаторы списка шаблонов должны быть различными. Внутри описания структуры идентификатор шаблона используется очень редко – в данном случае при вызове конструктора для формирования возвращаемого значения членом – функцией структуры: complex<T>. Здесь это необходимо потому, что возможно в принципе существование одновременно второго шаблона. А вот если в теле структуры описан только прототип члена – функции структуры, то при ее описании вне структуры обязательным является повторение template <class …>, причем идентификаторы шаблонов структуры и функции должны совпадать (строки №1 и №10). При создании экземпляра структуры после указания типа структуры в угловых скобках должен быть указан фактический тип данных, который подставляется вместо шаблона.
Например:
complex <float> a(1.,1.),b(2),c;
В результате происходит вызов конструктора (строка №3 для a и b, строка №2 для c), при этом значения в круглых скобках при идентификаторах a и b преобразуются к типу float ( а их изначальный тип – double и int), выделяется память под 3 структуры (8 байт на каждую), а поля структур получают значения, соответствующие комплексным числам 1.0 + 1.0i, 2.0 + 0.0i и 0.0 + 0.0i. Строка кода
complex <float> d = a * a;
приведет к вызову перегруженного оператора * , поля структуры d получат значения, соответствующие комплексному числу 0.0 - 2.0i. В строке №4 применен указатель this, который неявно получает каждый экземпляр структуры (класса, объединения). Этот указатель содержит адрес экземпляра структуры, а операция разыменования * , примененная к this , возвращает саму структуру. Список параметров функций, обеспечивающих перегрузку бинарных операций(т.е. операций над двумя операндами : см. строки №6,7,8,9) содержит только один – 2-ой операнд, поскольку 1-ым операндом является экземпляр структуры, вызывающий бинарную операцию. В условиях предыдущего примера
d = a + b;
равносильно
d.operator + (b);
Поэтому возможно вычисление выражения a + 4.0 , которое будет истолковано компилятором, как d.operator + (complex(4.0)) , где complex(4.0) является вызовом конструктора для преобразования 4.0 (тип double) к типу complex. Но лучше определить соответствующие функции – члены класса, например
complex operator+(T x)
В тоже время выражение 4.0 + a вызовет ошибку, так как первое слагаемое не является структурой типа complex и не может вызвать функцию operator+. Более того, невозможно создать соответствующую функцию – член структуры, обеспечивающую перегрузку операции +, поскольку в этом случае 1-ым операндом становится структура. Выход в том, чтобы создать не входящую в состав структуры функцию со списком аргументов – операндов в нужном порядке и перегружающую операцию в подобном случае:
template<class any>
complex<any> operator+(double x, complex<any> & cmpl2)
{ return complex<any>(x + cmpl2.re, cmpl2.im);}
Поскольку эта функция не является членом структуры complex , то идентификатор шаблона функции не обязан совпадать с идентификатором шаблона структуры .
Структура фактически отличается от класса доступностью компонентов по умолчанию (спецификатор доступа public: - т.е. общедоступны ), в то время, как у класса спецификатором доступа по умолчанию является private: -поля недоступны с помощью обычных методов “.” и “─>”. Обычно доступ private применятся для членов-данных, а public - для членов- функций (за исключением тех функций, которые упрощают написание основных членов- функций ). В этом случае чтение и запись членов-данных производится с помощью специально созданных для этого членов- функций. С моей точки зрения доступ private применять для членов-данных разумно в случаях
Данные не могут принимать все допустимые для их типа значения, и поэтому
проверку значений – кандидатов на присвоение выгодно сосредоточить в одном
месте – в члене функции . Примером такого члена- данного может служить сумма
средств на банковском счете – при любых операциях она не должна стать
отрицательной.
Член- данное получает значение только 1-раз, и в дальнейшем оно не может быть изменено. Пример – время создания экземпляра.
Член данное доступно остальной части программы только чтения. Пример - член данное – это количество элементов, размещенных в специально управляемом массиве (стеке, хеш-таблице), которое изменяется только членами-функциями при вставке и удалении элементов.
В заключение коснемся особенностей применения шаблонов функций . Шаблоны позволяют не создавать много перегруженных функций для аргументов различных типов :
int max (int a,int b); float max (float a, float b); и т.д. а только одну
template <class T> max (T a, T b) { return a > b ? a:b;}
Она может быть использована для переменных любых типов , если для них определена операция > . Однако при наличии шаблона компилятор требует точного соответствия типов остальных параметров в вызове функции описанию функции. Он не использует автоматического приведения типов, как в обычно, когда шаблона нет. Пример : дана функция
template <class T>
void far SortInsert (T[]item,long count,int down=0 );
В использующей ее программе нельзя объявить массив так
сonst long N = 1500;
int mas [N};
и вызвать функцию
SortInsert (mas,N);,
потому, что в описании функции N –переменная, а не константа
Следует использовать директиву препроцессора:
# define N 1500L //, где L или l указывает компилятору, что 1500 – это длинное целое.
int mas [N};
SortInsert (mas,N);
Препроцессор изменит вызов функции на
SortInsert (mas,1500L), что не вызовет у компилятора никаких возражений. Второй путь-создать динамический массив.
-***-
