русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Перегрузка функций

В “старом” языке С все имена функций должны быть уникальны в одном проекте. Это плохо и неудобно при работе с функциями, которые выполняют одинаковые или похожие действия с разными типами данных. Классический пример этого — стандартные функции abs(), labs(), fabs(), которые возвращают абсолютное значение, соответственно, целого, длинного целого (longint) и числа с плавающей точкой. В С++ можно определить несколько функций с одним и тем же именем, которые отличаются типами параметров и реже их количеством. Тогда говорят, что функции перегружены.

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

void RR ( int &, int &);

void RR ( float &, float &);

void RR ( char &, char &);

int main()

{ int i1=11, i2=22; RR(i1,i2);

cout<<"\ni1="<<i1<<" i2="<<i2<<endl;

float f1=3.4, f2=5.6; RR(f1,f2);

cout<<"\nf1="<<f1<<" f2="<<f2<<endl;

char c1='a', c2='b'; RR(c1,c2);

cout<<"\nc1="<<c1<<" c2="<<c2<<endl;

getch(); return 0;

}

void RR(int &u, int &v) {int t; t=u; u=v; v=t; }

void RR(float &u, float &v) {float t; t=u; u=v; v=t; }

void RR(char &c1, char &c2) { char c; c=c1; c1=c2; c2=c; }

Какой вариант функции RR из трёх вызывается в main()? Компилятор автоматически выберет необходимую версию функции на основании типа используемых в функции фактических параметров. Первый раз вызывается первый вариант функции для целых параметров, второй раз — для вещественных значений и, наконец, для символьных.

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

Нельзя, чтобы перегружаемые функции отличались только типом возвращаемых значений. Например, такая перегрузка функций int FUN2(int ); float FUN2 (int); компилятору не понравится !

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

Г л а в а 3
ВВЕДЕНИЕ В ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ

Изучение основных простейших понятий объектно-ориентированного программирования (ООП) проводится на примерах, которые можно проще запрограммировать без рассматриваемой методики. Из трёх свойств ООП в этой главе изучается только свойство инкапсуляции, которое означает следующее. Переменные (поля) и функции для работы с ними (методы) объединяются вместе с помощью специального структурированного типа данных, который называется класс. Кроме этого предполагается использование механизма защиты данных от несанкционированного доступа. Другими словами, с информацией, включённой в класс, разрешается работать только функциям данного класса.

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

§ 1. Примеры

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

class FirstCl // Описание класса с именем FirstCl

{ int a,b; // поля класса, т. е. переменные

public: // атрибут доступа (см.§ 2)

/* Заголовок конструктора. Это функция, которая имеет то же имя, что и класс; тип возвращаемого значения не записывается.*/

FirstCl(int x, int y);

/* Заголовки четырёх функций (методов) класса, оформленных как внешние. Они похожи на прототипы обычных самостоятельных функций, только поля класса, т. е. два целых числа, в качестве параметров не записываем */

void MyPrint();

int MySum();

bool MyTest();

/* Метод, который изменяет поля класса. Метод может использовать дополнительные параметры, которые не являются полями класса. У нас это число, на которое умножаем, наименьшее из двух чисел, или оба, если они одинаковы. */

void Change (int );

}; // конец описания класса

/* Текст внешнего конструктора. Перед именем внешнего конструктора и каждой внешней функции записываем имя класса и операцию “::” (разрешение области видимости). */

FirstCl::FirstCl (int x, int y)

{ a=x; b=y;

};

// Тексты четырёх внешних методов.

void FirstCl::MyPrint()

{ cout<<"\nThe first number "<<a<< " The second number "<<b <<endl;

} ;

int FirstCl::MySum()

{ return a+b;

};

/* Логическая функция, возвращающая true или false в зависимости от того, делится ли a на b без остатка (для 10 и 3 — false, 3 и 10 — false, 10 и 2 — true, 2 и 10 — false). */

bool FirstCl::MyTest()

{return !(a%b)? true : false;

};

void FirstCl::Change (int par)

{ if (a>b) b*=par;

else if (a<b) a*=par;

else { b*=par; a*=par;

}

};

/* В головной функции создаём объект, т. е. переменную типа класс, и вызываем включённые в класс методы. */

int main(int argc, char* argv[])

{ FirstCl obj(10,2);

/* Объявили объект obj и одновременно с этим вызвали конструктор, с помощью которого создаётся объект, и в класс передаются числа 10 и 2. Методы (функции) этого объекта будут выполняться для этих чисел. Повторно при вызове методов класса эти числа не передаём. Как и для самостоятельных функций, при объявлении объекта можно в качестве “фактических параметров” для конструктора передать не только константы, а и значения предварительно объявленных и определённых, например, введённых, переменных:

int A,B; cin>>A>>B;

FirstCl obj(A,B ); */

/* При вызове функций записываем имя объекта (obj), но не класса, и после точки имя функции (MyPrint). */

obj.MyPrint();

cout<<"\nSum "<<obj.MySum();

if (obj.MyTest()) cout<<"\n-----";

else cout<<"\n+++++";

obj.Change(10); cout<<"\nChanging ";

obj.MyPrint();

getch(); return 0;

}

При вызове функций класса есть аналогия с обычными самостоятельными функциями, не членами класса. Функции типа void вызываются отдельно (obj.MyPrint(); obj.Change(10);). Вызов функции с возвращаемым с помощью return единственным результатом записывается непосредственно в выражении, а значит, в операторе, в котором используется значение функции

(if (obj.MyTest() ) …).

Ещё раз обратим внимание на следующие особенности вызова методов класса:

· перед именем метода записывается имя объекта (а не класса!) и после точки имя метода. При необходимости в скобках записываем фактические параметры (см. функцию Change);

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

П р и м е р 2. Разработать класс для работы с одномерным массивом наибольшей размерности 20. В отличие от первого примера, все функции оформим как встроенные. Их назначение приведено в комментариях:

#define MaxSize 20

/*Определили макрос, используемый в качестве наибольшей размерности массива */

class ClArray

{ int n;

float A[MaxSize];

public:

/* Конструктор инициализирует только одно поле — размерность массива. При этом осуществляется проверка передаваемого значения. Массив, который является вторым полем класса, определяется с помощью функции MyInp */

ClArray (int m)

{ n=m; if (n>MaxSize || n<1) n=10;

} ;

// Функция (метод) для ввода массива.

void MyInp()

{ int x=1, y; y=wherey();

for(int i=0; i<n; i++, x+=7)

{ gotoxy(x,y); cin>>A[i];

}

} ;

/* Функция (метод) для “цветного” вывода массива. При этом числа num выводятся цветом С1, а остальные — цветом С2 */

void MyOut(int C1, int C2, float num)

{ cout<<endl;

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

{ if (A[i]==num) textcolor ( C1 );

else textcolor ( C2 );

cprintf ( "%10.4f", A[i] );

}

}

/* Логическая (булевская) функция (метод), возвращающая true или false в зависимости от того, найдено число t в массиве или нет */

bool Test (float t)

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

if (A[i]==t) return true;

return false;

}

/* Функция (метод), которая в массиве каждое наибольшее число меняет на наименьшее и наоборот. Кроме того, функция возвращает наибольшее и наименьшее значения. */

void MyChange ( float &mx, float &mn)

{ mx=mn=A[0];

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

{ mx= A[i]>mx? A[i] : mx;

mn= A[i]<mn? A[i] : mn; }

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

if (A[i]==mx) A[i]=mn;

else if (A[i]==mn) A[i]=mx;

} // end of the last function

} ; // end of class

/* В головной функции в цикле создаём объекты, то есть работаем с несколькими одномерными массивами, пока не введём в качестве размерности число 0. */

int main()

{ int N=5; float num;

do { ClArray ObArr(N);

ObArr.MyInp(); // ввод массива

while(1) // цикл для поиска нескольких чисел в массиве

{ cout<<"\n Number for find (1000 -- exit)"; cin>>num;

if (num==1000) break;

ObArr.MyOut ( 2, 5, num); // вывод массива разным цветом

if ( ObArr.Test ( num ) )

cout <<" \n Find the number " ;

else cout<<"\n Do not find the number ";

cout<<num;

}

float MyMx, MyMn;

/* Изменяем массив и возвращаем в функцию main наибольшее (MyMx) и наименьшее (MyMn) значения. */

ObArr.MyChange(MyMx, MyMn);

/* Вывод массива с выделением другим цветом наибольшего значения (MyMx). */

ObArr.MyOut(2,5,MyMx);

/* Вывод массива с выделением другим цветом наименьшего значения (MyMn). */

ObArr.MyOut(2,5,MyMn);

cout<< "\n Size of array (0 -- exit)"; cin>>N;

} while ( N);

return 0;

}

§ 2. Класс. Поля и методы класса

Класс — это сложный (структурированный, составной) тип данных, объединяющий переменные, которые называют полями класса, и функции для работы с этими полями, которые называют методами класса. При этом методы могут только использовать поля (вывод, вычисление суммы двух чисел в прим. 1), получать их значения (ввод), преобразовывать поля (функция MyChange в прим. 2) и выполнять другие виды обработки.

Классы могут быть стандартными. Их количество в современных системах резко возрастает. Как видно из примеров, есть возможность разрабатывать собственные классы.

В качестве полей класса могут быть переменные как простых, так и структурированных типов. Объявляются поля по обычным правилам. Одинаковые типы при этом можно не повторять. Поля играют роль глобальных переменных для функций данного класса. Поэтому повторно объявлять их в функциях класса или передавать в качестве параметров методов не надо. Начальные значения полей можно определить с помощью конструктора (подробности в § 3) или с помощью других функций класса, например, функции ввода (MyInp во втором примере).

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

class FirstCl

{ int a,b; …}

равносильно class FirstCl

{ private:

int a,b; … }

Это означает, что поля принадлежат классу, т. е. они доступны (“видны”) только в методах данного класса. (Напомним, что наследование пока на первом курсе не учитываем). В других функциях, не принадлежащих классу, в том числе в main, эти переменные нельзя использовать ни напрямую, ни через объект данного класса. Например, в main нельзя написать ни a=5, ни cout<<obj.a. Благодаря этому реализуется принцип инкапсуляции, первый принцип ООП. Согласно ему, с полями класса можно работать только в методах этого класса, а в других функциях, не принадлежащих этому классу, в том числе и в main, они недоступны.

Методы класса, как правило, записываются после ключевого слова public (общедоступный), что означает следующее. Функции класса с таким атрибутом доступа можно вызывать как из методов данного класса напрямую, без записи имени объекта (MyOut()), так и из других функций, не являющихся членами этого класса, через объект данного класса (obj.MyPrint()).

Если поля класса объявить с ключевым словом public, то их можно использовать не только в функциях данного класса, но и в других функциях. А это противоречит принципу инкапсуляции ООП. Другими словами, несмотря на использование типа class, такое программирование не является объектно–ориентированным.

Некоторые методы можно объявить с атрибутом доступа private. Тогда их можно вызывать только из методов этого класса. Другими словами, если мы продадим наш класс, то такие функции недоступны для покупателя. Пользоваться ими может разработчик класса, если в нескольких его методах необходимо программировать одинаковые действия.

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

· как внутренние (встроенные) методы (см. прим. 2);

· или внешние методы (прим. 1).

Сравнительная характеристика такая же, как и для обычных функций (см. 6.1 гл. 2).

§ 3. Создание объектов. Конструктор

Объект— это переменная типа класс. Класс — это абстрактное понятие, шаблон, заготовка для создания объекта. Оперативная память для класса, как и для другого типа, не отводится. Объект существует реально, физически после его создания, то есть он занимает определённую оперативную память.

Объявляется объект одним из следующим способов:

· имя_класса имя_объекта (параметры), если конструктор имеет параметры;

· имя_класса имя_объекта, если конструктор не имеет параметров или все параметры имеют значения по умолчанию или конструктора нет.

В любом случае, во-первых, создаётся объект и, во-вторых, вызывается конструктор, если он есть. В первом варианте конструктору передаются параметры.

Конструктор — это специальная функция, которая служит для инициализации, т. е. задания начальных значений всех (прим. 1) или некоторых (прим. 2) полей класса. В прим. 2 с помощью конструктора задали только размерность массива, а массив определили с помощью функции класса. Теоретически в этой функции разрешается выполнять любые действия, но это делать не рекомендуется. Конструктор обладает следующими особенностями:

· он не имеет никакого типа возвращаемого значения, в том числе void;

· имя конструктора совпадает с именем класса;

· вызывается конструктор автоматически при объявлении объекта. Благодаря последним двум особенностям конструктор обязательно будет выполнен, так как мы не можем забыть объявить объект, а значит вызвать конструктор. В противном случае компилятор напомнит нам об этом.

Конструктор может быть с параметрами или без них. В первом случае число 10 (см. прим. 1) через параметр x передаётся в качестве значения поля a по следующей “цепочке”: 10 –> x –> a. Аналогично число 2 инициализирует поле b: 2 –> y –> b. То есть числа 10 и 2 играют роль фактических параметром при вызове специальной функции, которой является конструктор. При вызове методов повторно передавать эти же значения, как это имело место в случае самостоятельных функций, не членов класса, не надо.

Если конструктор не имеет параметров, то поля класса можно определить с помощью констант. Например, в прим. 1 конструктор может быть таким:

FirstCl { a=2; b=5;}

Тогда вызывается он так: FirstCl obj; т. е. поля из головной функции не передаются, так как они определены с помощью констант в тексте конструктора.

Параметры конструктора, как и любой другой функции (см. §6 гл. 2), могут иметь значения по умолчанию. Например, в первом примере заголовок конструктора может быть таким: FirstCl ( int x=2, int y=5). Его текст при этом не меняется. Тогда объявить объект и вызвать конструктор можно одним из следующих способов:

a) FirstCl obj1; поля получат значения a=2, b=5;

b) FirstCl obj2 (30); значения полей будут такими: a=30, b=5;

c) FirstCl obj3 (30, 40); поля получат значения a=30, b=40;

Конструктор, как и любая другая функция (см. §6 гл. 2), может быть перегружен, то есть можно записать несколько конструкторов, которые отличаются параметрами. Например, в одном и том же классе можно записать два следующих конструктора с одинаковыми, конечно, именами, совпадающими с именем класса:

FirstCl { a=2; b=5;} и FirstCl (int x, int y) { a=x; b=y; };

Тогда объявить объект можно одним из следующих способов:

a) FirstCl obj; при этом поля получат значения 2 и 5;

b) int X, Y ; cin>>X>>Y ; FirstCl obj (X, Y); при этом поля будут проинициализированы введёнными значениями.

Но вместо такой перегрузки конструктора лучше использовать параметры по умолчанию.

Просмотров: 500


Вернуться в оглавление



Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.