Аргументы функций, задаваемые по умолчанию

В С++ возможна установка значений параметров функции по умолчанию, если при вызове функции аргументы не были указаны.

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

void f (int i=10)

{ }

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

f(1); // передаётся значение аргумента i, равное 1

f( ); // передаётся значение i по умолчанию, равное 10.

Рассмотрим пример полезной функции, отсутствующей в библиотеке Borland C++, эта функция при работе с текстовым экраном выводит строку, начиная с заданного места экрана.

void string (char *str, int x = -1, int y = -1)

{

if (x == -1) x = wherex ( ); // где x?

if (y == -1) y = wherey ( ); // где y?

gotoxy (x,y);

cout << str;

/* Функции wherex и wherey возвращают текущие координаты курсора Х и Y соответственно, их прототипы находиятся в файле conio.h */

}

При вызове функции stringxy( ) из функции main ( ) может быть указано разное количество аргументов. (Это допускается реализацией языка С ++.) Если определены все три параметра, то функция работает естественным образом. Если два, то значение последнего устанавливается по умолчанию равным – 1, а в результате работы функции устанавливается равным значению координаты у курсора. Если указан один параметр, то x и y определены по умолчанию. Неверным будет вызов:

stringxy (« Линия 13\ n», , 13); // х задать по умолчанию нельзя.

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

Объявления void stringxy (char *str, x= -1,y);

void stringxy (char *str, x= -1,y= -1);

не верны, поскольку не задано значение первого параметра *str.

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

При объявлениях

void f (void)

void f (int i=0);

и вызове f ( ) компилятор выдаст ошибку, т.к. появляется неоднозначность выбора функции.

Устанавливать значения параметров по умолчанию можно и для класса. Так в примере kons1.cpp (см. С 39) объявлены два конструктора, но можно сделать с помощью одного конструктора с параметром, задаваемым по умолчанию:

queue :: queue (int i=0)

{ rloc=sloc=0;

id=i

cout<< «очередь»<< id<< инициализирована \n»;);

}

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

queue а(5), b;

значение id для первого объекта (а) будет = 5, для второго(b) =0.

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

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

Одним из важнейших принципов объектно-ориентированного программ-мирования является возможность защиты данных от несанкционированного использования. Он реализуется благодаря режиму доступа к членам класса. Члены класса с режимом доступа private могут использовать функции- члены только этого класса. Однако, возникают ситуации, когда необходимо, чтобы к приватной части имела доступ функция, не являющейся членом этого класса. Это можно сделать при объявлении класса, объявив функцию ключевым словом friend.

Пример.

Class c1

{ ….

public:

friend void frd (void); // функция frd объявлена как дружественная

…. // функция класса с1

}

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

Пример объявления двух классов, в каждом из которых объявлена дружественная функция int same_ color (line l, box b), не являющаяся членом ни первого, ни второго класса.

class line;

Class box

{ int color;

int upx, upy;

int lowx,lowy;

public:

friend int same_color(line l, box b);

void set_color(int c);

void define_box(int x1, int y1, int x2, int y2);

void show_box(void);

};

Class line

{ int color;

int startx, starty;

int len;

public:

friendint same_color(line l, box b);

void set_color(int c);

void define_line(int x, int y, int l);

void show_line(void);

};

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

Дружественная функция в примере должна сравнивать значение переменной color в каждом из классов и возвращать значение «едница», если их значения одинаковы, и ноль в противном случае. Она является внешней для классов и записывается в следующем виде.

int same color(line l, box b)

{

if (l, color = =b, color) return 1;

return 0;

}

Если класс используется в другом классе до того, как объявлен его шаблон, необходимо сделать предварительное объявление.

Например, класс line используется в классе box до объявления класса line, поэтому надо ввести предварительное объявление класса line в начале программы (class line;). Оно похоже на объявление прототипа функций.

Пример программы использования дружественных функций имеет вид

#include <iostream.h> (drug1.cpp)

#include <conio.h>

class line;

Class box

{

int color;

int upx, upy;

int lowx,lowy;

public:

friendint same_color(line l, box b);

void set_color(int c);

void define_box(int x1, int y1, int x2, int y2);

void show_box(void);

};

Class line

{

int color;

int startx, starty;

int len;

public:

friend int same_color(line l, box b);

void set_color(int c);

void define_line(int x, int y, int l);

void show_line(void);

};

// Описание функций членов класса box

void box::set_color(int c)

{ color=c;}

void box::define_box(int x1, int y1, int x2, int y2)

{

upx=x1;

lowx=x2;

upy=y1;

lowy=y2;

}

void box::show_box(void)

{

window(upx,upy, lowx,lowy);

textbackground(color);

clrscr();

textbackground(BLACK);

window(1,1,80,25);

}

// Описание функций членов класса line

void line:: set_color(int c)

{ color=c; }

void line:: define_line(int x, int y, int l)

{

startx=x;

starty=y;

len=l;

}

void line::show_line(void)

{

textcolor(color);

gotoxy(startx, starty);

for(int k=0; k<len; k++)

cprintf("%c",'-');

textcolor(WHITE);

}

// Описание дружественной функций

int same_color(line l, box b)

{ if (l.color==b.color) return 1;

return 0;

}

void main(void)

{

line l;

box b;

textbackground(BLACK);

clrscr();

b.define_box(5,5,25,10);

b.set_color(RED);

l.define_line(5,15,30);

l.set_color(BLUE);

b.show_box();

l.show_line();

gotoxy(1, 1);

if (same_color(l,b) )

cputs("Same colors\n");

else cputs("Different colors\n");

getch();

b.define_box(45,6,70,11);

b.set_color(GREEN);

l.define_line(45,16,30);

l.set_color(2);

b.show_box();

l.show_line();

gotoxy(41, 2);

if (same_color(l,b) )

cputs("Same colors\n");

else cputs("Different colors\n");

getch();

}

Дружественной может быть не только внешняя функция, но и функция – член другого класса.

В примере можно было объявить функцию same_color( ) функцией - членом класса box , а в классе line - дружественной функцией:

class line;

Class box

{

int color;

int upx, upy;

int lowx,lowy; //drug2.cpp

public:

int same_color(line l);

void set_color(int c);

void define_box(int x1, int y1, int x2, int y2);

void show_box(void);

};

Class line

{

int color;

int startx, starty;

int len;

public:

friend int box::same_color(line l);

void set_color(int c);

void define_line(int x, int y, int l);

void show_line(void);

};

В классе line указано полное имя функции box::same_color( ). Кроме того, можно не указывать в качестве аргумента объект класса box . Выглядеть новая функция box::same_color( ) будет по-другому [7]:

int box:: same_color(line l)

{

if ( l.color == color ) return 1;

return 0;

}

Дружественные классы

При объявлении класса можно объявить сразу все функции – члены другого класса дружественными одним объявлением.

class X {…..};

Class Y

{…..

friend class X;

};

Объявление класса дружественным предполагает, что приватные, защищенные члены класса X могут использоваться в классеY.

Наследование классов

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

Основная форма наследования:

class <имя наследующего класса>: режим доступа < наследуемый класс> {¼};

или

class <производный> <базовый>;

Класс, который наследуется называется базовым классом, наследующий – производным (или потомком).

Режим доступа – это одно из ключевых слов private, protected, public.

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

Когда один класс наследует другой класс, все приватные элементы базового класса недоступны для производного.

class X // Базовый класс

{ // Приватные элементы:

int i;

int j;

public:

void get_ij(void);

void put_ij(void);

};

class Y: public X // Производный класс Y

{

int k;

public:

int get_k(void);

void make_k(void);

};

В этом примере Х - базовый класс, Y- производный класс. Члены класса Y могут использовать общие функции get_ij( ) и put_ij ( ), но они не могут использовать i и j, т. к. они приватные для Х (частный) и соответственно, недоступны для функций get_k ( ), make_k ( ) класса Y.

Чтобы обеспечить доступ членов функций класса Y к элементам i j класса Х, надо их в классе Х объявить защищенными (protected:). Теперь члены класса Y имеют доступ к членам i и j класса Х. В то же время i и j, остаются недоступными для остальной части программы. Доступ наследуется к элементам, объявленным защищенными или общими, но не наследуется для приватных элементов.

Наследование – один из главных механизмов объектно-ориентированного программирования. С его помощью можно разрабатывать очень сложные классы, продвигаясь от общего к частному, а также «наращивать» уже созданные, получая из них новые классы, немного отличающиеся от исходных.

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

Если режим доступа отсутствует, то предполагается по умолчанию режим доступа public при производном классе структура, и private, если производный класс – класс.

При режиме доступа public, то общие и защищенные члены базового класса остаются общими и защищенными членами производного класса. Если режим доступа private, то все общие и защищенные члены базового класса остаются приватными элементами производного класса. Если режим доступа protected, то все общие и защищенные члены базового класса становятся защищенными членами производного класса.

Доступ наследуется к элементам, объявленным защищенными или общими, но не наследуется для приватных элементов. Сказанное выше иллюстрируется таблицей 5.

Таблица 5

Режимы доступа

Режим доступа к элементу в базовом классе Режим доступа при наследовании класса Режим доступа к элементу в производном классе
Private   publuc Не доступен
Protected Protected
public Public
Private   protected Не доступен
Protected Protected
public Protected
Private   private Не доступен
Protected Private
public Private

Пример передачи режима доступа к переменным при наследовании [7]

#include <iostream.h> nasled.cpp

Class X

{ protected:

int i; // i, j класса X protected в классе Y

int j;

public:

void get_ij(void);

void put_ij(void);

};

Class Y: public X

{ int k; // private

public:

int get_k(void);

void make_k(void);

};

class Z: public Y // Z имеет доступ к переменным i, j класса X

{ public: // Z не имеет доступа к переменной k класса Y

void f(void);

};

void X:: get_ij(void)

{ cout<< "Введите два числа";

cin>> i >> j;

}

void X:: put_ij(void)

{ cout<< "i=" << i << "j="<< j << "\n"; }

int Y:: get_k(void)

{ return k; }

void Y:: make_k(void)

{ k = i*j; }

void Z:: f(void)

{ i=2; j=3; }

void main(void)

{

Y var;

Z var2;

var.get_ij();

var.put_ij();

var.make_k();

cout<< var.get_k();

cout<< "\n";

var2.f();

var2.put_ij();

var2.make_k();

cout<< var2.get_k();

cout<< "\n";

}

Если заменить режим доступа при наследовании класса Х на private, то функция void Z:: d(void) не будет иметь право доступа к переменным i, j.

Замена режим доступа при наследовании класса Z на private, не изменяя режим доступа при наследовании класса Х действие примера по сравнению с начальным не изменит.

Наши рекомендации