Создание объектов. Конструктор
Объект— это переменная типа класс. Класс — это абстрактное понятие, шаблон, заготовка для создания объекта. Оперативная память для класса, как и для другого типа, не отводится. Объект существует реально, физически после его создания, то есть он занимает определённую оперативную память.
Объявляется объект одним из следующих способов:
1) имя_класса имя_объекта (параметры);
если конструктор имеет параметры;
2) имя_класса имя_объекта;
если конструктор не имеет параметров или все параметры имеют значения по умолчанию или конструктора нет.
В любом случае, во-первых, создаётся объект и, во-вторых, вызывается конструктор, если он есть и передаются параметры (см. 1)).
Конструктор — это специальная функция, которая служит для инициализации (задания начальных значений) всех (пример 1) или некоторых (пример 2) полей класса. В примере 2 с помощью конструктора задали только размерность массива, а массив определили с помощью функции класса. Теоретически в этой функции разрешается выполнять любые действия, но это делать не рекомендуется. Конструктор имеет следующие особенности:
· он не имеет никакого типа возвращаемого значения, в том числе void;
· имя конструктора совпадает с именем класса;
· для локальных объектов, созданных, как в наших примерах, в main, конструктор вызывается автоматически при объявлении объекта. Благодаря последним двум особенностям конструктор обязательно будет выполнен, так как мы не можем забыть объявить объект, а, значит, вызвать конструктор. В противном случае компилятор напомнит нам об этом.
Конструктор может быть с параметрами или без них. В первом случае число 10 (см. прим. 1) через параметр x передаётся в качестве значения поля a по следующей “цепочке”: 10 –> x –> a. Аналогично число 2 инициализирует поле b: 2 –> y –> b. То есть числа 10 и 2 играют роль фактических параметром при вызове специальной функции, которой является конструктор. При вызове методов повторно передавать эти же значения, как это имело место в случае самостоятельных функций, не членов класса, не надо.
Кроме констант с помощью конструктора в класс можно передать переменные (введённые A и B в примере 1) или выражения соответствующего типа.
Если конструктор не имеет параметров, то поля класса можно определить, например, с помощью констант или каким-нибудь другим способом. Например, в примере 1 конструктор может быть таким: FirstCl { a=2; b=5;} Тогда вызывается он так: FirstCl obj; т. е. поля из головной функции не передаются, а определяются с помощью констант в тексте конструктора. Заметим, что при объявлении объекта и одновременном вызове конструктора без параметров пустые круглые скобки не пишем.
Параметры конструктора, как и любой другой функции, могут иметь значения по умолчанию. Например, в первом примере заголовок конструктора может быть таким: 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;
d) нельзя, как и для обычных функций: FirstCl obj4 ( , 40);
Конструктор, как и любая другая функция (см. §7 гл. 3), может быть перегружен, то есть можно записать несколько конструкторов, которые отличаются параметрами. Например, в одном и том же классе можно записать два следующих конструктора:
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);// поля получат значения X,Y.
Упражнения и тесты
1. Дан код:
const Nmax=20;
class cla
{ int a[Nmax]; int n; public:
cla (int); void out(int ,int ); float aver();} ; //1
cla::cla(int size) //2
{ n=size; for(int i=0; i<n; i++) //3
if (i%2) a[i]=-i*10; else a[i]=i*5; };
void cla::out(int c1, int c2) //4
{ for(int i=0; i<size; i++) //5
{ if(a[i]<0) textcolor(c1); else textcolor(c2);
cprintf(" %5d ",a[i]); }
} ;
float cla::aver() //6
{ float k1=0,s1=0;
for (int i=0;i<size;i++) if(a[i]>0) { k1++; s1+=a[i]; } //7
if (k1>0) return s1/k1; else return 0; } ;
int main() { int k=10, col1=12, col2=11; cla ob(k); //8
cout<< ob.out(col1,col2); //9
float Res= ob.aver() ; //10
printf( Res? "\n Yes: sr1= %10.3f \n" : "\nNo" ,Res); //11
getch(); return 0;
};
1) Найти номера строк с ошибками, кратко объяснить их и исправить.
2)После изменения или если ошибок нет, записать, что будет выведено.
3) Используя параметры по умолчанию, записать один конструктор, чтобы можно было создать любой из трёх объектов:
/* a */ cla Arr1(10); /* b */ int N; cin>>N; cla Arr2(N);
/* c */ cla Arr3; //В этом варианте массив должен иметь размерность 5.
4) Методы и конструктор записать встроенными.
Ответы. 1) В строках //5 и //7 переменная size недоступна, так как она видна только в конструкторе. Правильно: for(int i=0; i<n; i++). В строке //9 метод типа void, как и обычную функцию, нельзя вызывать в выражении, а, значит, и в cout. Правильно: ob.out(col1, col2);
2) Для получения ответа выполните программу с учетом ошибок.
3)Внутри класса записываем встроенный конструктор
cla (int size=5) {/* Тот же текст */}
Тогда в вариантах a и b изменим размерность 5 на 10 и введённое N, а в варианте с по умолчанию размерность останется 5.
4) Для его получения сравните примеры 1 и 2 §3.
2. Дан код:
class MyD
{ public: int d, m; //1
MyD ( int d2=1, int m2=9) { d2=d; m2=m; }; //2
void show() { cout<<"day "<<d2<<" month "<<m2<< endl; }//3
};
int main() { cin>>d; cin>>m; //4
MyD d1(d, m); //5
MyD.show(); //6
getch(); return 0; }
1) Найти номера строк с ошибками, кратко объяснить их и исправить.
2) После изменения или если ошибок нет, записать, что будет выведено.
3) Метод и конструктор записать как внешние.
4) Перегрузить конструктор таким образом, чтобы можно было создать объект одним из следующих способов:
/*a*/ MyD D1(20,12); /*b*/ int u, v, w; cin>>u>>v; MyD D2(u,v);
/* c */ MyD D3; /* Должна инициализироваться дата 8.03.Параметры по умолчанию не использовать. */
5) Изменить программу таким образом, чтобы она работала и соответствовала принципу инкапсуляции ООП.
Ответы. 1) Строку //2 компилятор пропустит, но выполняться будет неправильно, так как надо проинициализировать поля класса d и m. Поэтому надо записать: MyD ( int d2=1, int m2=9) { d=d2; m=m2; };. В строке //4 поля класса d и m недоступны, несмотря на то, что в классе объявлены с атрибутом public. Для исправления этой ошибки в main объявляем переменные с теми же (d и m) или другими именами: int d, m;. В строке //6 при вызове метода перед точкой записывается имя объекта, а не класса: d1.show();
2) Для получения ответа выполните программу с учетом ошибок.
3) — 5)Внутри класса записываем заголовки перегружаемых конструкторов и метода, а вне класса — их тексты. Чтобы программа соответствовала принципу инкапсуляции ООП, надо атрибут public поменять на private, который можно не писать явно. Для конструкторов и метода атрибут public сохраняем. Получим
class MyD
{ int d, m;
public:
MyD ( int, int) ; MyD ();
void show();
};
MyD::MyD ( int d2, int m2) { d=d2; m=m2; };
MyD::MyD ( ) { d=8; m=3; };
void MyD::show() { cout<<"day "<<d<<" month "<<m<< endl; }
int main()
{ int d, m; cin>>d; cin>>m;
MyD d1(d, m); d1.show();
MyD d2; d2.show();
getch(); return 0;
}
3. Дан код:
class cl3
{ int a; public:
cl3 () { a=123; return a;} //1
cl3 (int x) { a=x;} //2
bool chet() { return !(a%2);} //3
void show(); }; //4
void show() { printf("\n%d ", a);}; //5
void main() { int q=788; cl3 ob1(q); //6
int R1=cl1(); cout<<endl<<R1; //7
bool B= cl3.chet(); //8
if (B) cout <<"\nYes\n"; else cout<<"\nNo\n";
ob1.show(); //9
cl3 ob2(); //10
int R2=ob2; //11
cout<<endl<< ob2.chet(); //12
ob2.show(); //13
a+=10; cout<<a; getch(); //14
}
1) Найти номера строк с ошибками, кратко объяснить их и исправить.
2) После исправления ошибок или, если их нет, записать, что будет выведено.
Ответы. 1) В строке //1 конструктор не должен возвращать значения. Правильно так: cl3 () { a=123; } По правилам оформления внешних функций строку //5 надо записать так: void cl3::show() { printf("\n%d ", a);};. При объявлении (создании) объекта в строке //10 и одновременном вызове конструктора без параметров, в отличие от вызова похожих функций круглые скобки не пишем: cl3 ob2; В строке //8 при вызове метода надо записать имя объекта, а не класса: bool B= ob1.chet(); Вызов метода, отличного от void с помощью присваивания можно совместить с объявлением переменной.Так, как записано в //7 и //11,нельзя из класса, а точнее из объекта “вытащить”значение поля его и использовать дальше в другой функции, не принадлежащей классу.Для этого надо составить метод int A() {return a;} и вызвать его из другой функции, не обязательно из main(), по общим правилам: int R1=ob1.A(); и в //8 int R2=ob2.A();. Аналогично изменитьзначение поля (см. //14) можно также только с помощью метода класса, так как в main() поле a недоступно: int Change () { return a*10;}; Вызов: R1=ob1. Change();
2) Выполните эту программу с учётом изменений и получите ответ.
4. Выберите правильные утверждения:
1) Класс – это структурированная (сложная, составная) переменная, объединяющая поля и методы.
2) Класс – это структурированный (сложный, составной) тип данных (шаблон, заготовка), объединяющий переменные (поля) и функции (методы).
3) Класс – это функция, используемая в ООП.
4) Класс занимает определенный объем оперативной памяти.
5) Класс – абстрактное понятие. Оперативная память для него не резервируется.
1.Выберите правильные утверждения:
1) Объект – это структурированная (сложная, составная) переменная типа класс.
2) Объект – это структурированный (сложный, составной) тип данных (шаблон, заготовка), объединяющий переменные (поля) и функции (методы).
3) Объект – это функция, используемая в ООП.
4) Объект занимает определенный объем оперативной памяти.
5) Объект – абстрактное понятие. Оперативная память для него не резервируется.
2.Выберите правильные утверждения:
1) Поля класса без дополнительного (повторного) объявления можно использовать в методах данного класса.
2) Поля класса являются глобальными для методов данного класса.
3) Поля класса по умолчанию имеют атрибут доступа private.
4) Поля класса по умолчанию имеют атрибуты доступа public.
5) Атрибут доступа private нельзя записывать явно перед объявлением полей класса.
6) Поля класса можно объявить с ключевым словом public, но это противоречит принципу инкапсуляции ООП.
7. Атрибут доступа private для полей класса означает, что … (выберите правильные утверждения):
1) В методах данного класса поля можно использовать.
2) В методах данного класса поля нельзя использовать.
3) В функциях, не принадлежащих данному классу (например, в main) поля можно использовать. При этом доступ осуществляется так:
имя_объекта.имя_поля.
4) В функциях, не принадлежащих данному классу (например, в main) поля можно использовать. При этом доступ к полю осуществляется просто по имени.
8. Атрибут доступа public для методов класса типа void означает, что … (выберите правильные утверждения):
1) в функциях, не принадлежащих классу (например, в main), метод можно вызвать, записав отдельно, не в выражении:
имя_объекта.имя_метода (параметры);
2) в функциях, не принадлежащих классу (например, в main), метод можно вызвать, записав отдельно, не в выражении:
имя_класса.имя_метода (параметры);
3) в функциях, не принадлежащих классу (например, в main), метод можно вызвать, записав в выражении (например, в присваивании):
r=имя_объекта.имя_метода (параметры);
4) в функциях, не принадлежащих классу (например, в main), метод можно вызвать, записав в выражении (например, в присваивании):
r=имя_класса.имя_метода (параметры);
9.Атрибут доступа public для методов класса, отличного от void (например, int) означает, что …(выберите правильные утверждения): (см. варианты из предыдущего теста)
10.Что такое конструктор? Варианты ответов:
1) Конструктор – это стандартный (встроенный) тип данных.
2) Конструктор – это специальная, используемая по определенным правилам, переменная.
3) Конструктор – это специальная, записываемая и вызываемая по определенным правилам функция, включаемая в описание класса.
4) Конструктор – это специальная функция типа void, включаемая в описание класса.
11.Выберите правильные утверждения:
1) Если поле класса Cl1 имеет тип int, то заголовок конструктора можно записать, например, так: int Cl1 (int x);
2) Конструктор всегда имеет только тип void и не может иметь других типов (int, float и др.).
3) Конструктор имеет то же имя, что и класс, частью которого он является.
4) Конструктор не имеет возвращаемого значения. Ни тип void, ни другой тип (int, float и др.) для него не записывается.
12.Выберите правильные утверждения:
1) Конструктор всегда должен иметь параметры, количество которых должно совпадать с количеством полей класса.
2) Конструктор всегда должен иметь параметры, количество которых не обязательно должно совпадать с количеством полей класса.
3) Конструктор не может иметь параметров.
4) Конструктор может иметь параметры или может быть без них.
13.Выберите правильные утверждения:
1) Для локальных объектов конструктор вызывается при выполнении инструкции объявления объекта.
2) Заголовок внешнего метода, записываемый перед текстом (описанием) функции, имеет следующий вид: имя_класса::имя_функции (параметры)
При этом параметры не обязательны.
3) Заголовок внешнего метода, записываемый перед описанием функции, имеет следующий вид: тип имя_класса::имя_функции (параметры),
где тип — void или отличный от void (int, float, char и др.). При этом параметры не обязательны.
4) Заголовок внешнего метода, записываемый перед текстом (описанием) функции, имеет следующий вид: имя_объекта::имя_функции(параметры)
При этом параметры не обязательны.
5) Заголовок внешнего метода, записываемый перед описанием функции, имеет следующий вид: тип имя_ объекта::имя_функции (параметры),
где тип — void или отличный от void (int, float, char и др.). При этом параметры не обязательны.
Задачи
1. Составить класс для работы с двумя целыми числами. Предусмотреть конструктор, методы для вывода двух чисел, вычисления их суммы, вычисления произведения этих же чисел. В головной функции ввести два числа, создать объект, вычислить и вывести их сумму и произведение. Все методы оформить как внешние.
class firstB
{ int a, b; /* Поля класса с атрибутом доступа private */
public: /* Атрибут доступадля конструктора и методов */
firstB(int, int ); /*Заголовок конструктора с двумя параметрами */
/* Заголовки остальных методов (функций) */
void show();
int sum();
int mult();
}; // the end of class
firstB:: firstB(int u, int v)
{ a=u; b=v; }; /*Tекст конструктора */
/* Tексты остальных функций, оформленных как внешние */
void firstB:: show()
{ cout<<"\nThe first number= "; cout<<a;
cout<<", the second number= "; cout<<b; };
int firstB:: sum() {return a+b; } ;
int firstB:: mult() { return a*b; };
int main() { int A, B; cout<<"A= "; cin>>A;
cout<<"B="; cin>>B;
firstB ob (A, B); /* Cоздание объекта и передача ему введенных A и B*/
/* Или можно в объект передать константы: firstB ob(5,4); */
ob.show(); // Вызов метода show
/* Вариант 1: полученные с помощью методов результаты (сумму и произведение), как и для обычных функций, сохраняем в переменных, а затем используем, т. е. выводим.*/
int S= ob.sum(), M=ob.mult();
cout<<" \n The sum= "<<S << " The product= "<<M<<endl;
/* Вариант 2: Результаты функций сразу выводим без сохранения в переменных.*/
cout<<"\nThe sum== "<<ob.sum()
<< " The product= "<<ob.mult(); getch(); return 0;
};
2. Составить класс для работы с двумя целыми числами, предусмотрев вычисление и вывод их суммы и произведения со следующими отличиями, по сравнению с предыдущей программой:
· один и тот же метод и вычисляет, и выводит сумму двух чисел с определенной, указанной в методе, позиции окна. Поэтому этот метод не имеет параметров;
· метод вычисляет и выводит произведение, но позиция вывода передается как параметр метода при ее вызове;
· вывод осуществляется разными цветами, которые, как и исходные числа, являются полями класса. Числа и цвета передаются с помощью конструктора, который поэтому имеет четыре поля;
· все методы встроенные.
class Cl2 { int a, b, c1, c2;
public:
Cl2(int x, int y, int s1, int s2)
{ a=x; b=y; c1=s1; c2=s2; };
void sum()
{ textcolor(c1); gotoxy(5,2);
cprintf("x=%d y=%d sum=%d\n", a,b, a+b); } ;
void mult(int x, int y)
{ textcolor(c2); gotoxy(x,y);
cprintf("x=%d y=%d mult=%d\n", a, b, a*b);
}; // the end of function mult
}; // the end of class
int main() { Cl2 ob (4,5,9,10);
ob.sum(); ob.mult(10,6);
getch(); return 0; };
3. Составить класс для работы с одномерным массивом максимальной размерности 10, предусмотрев следующие методы:
· конструктор для проверки и задания размерности массива. Если размерность массива неположительная или больше 10, то работаем с массивом размерности 10;
· ввод массива;
· поиск первого по порядку положительного числа и его индекса.
const nmax=10;
class clarray1
{ int x[nmax]; int n;
public:
clarray1(int v=7) { n=v; if (n<1 || n>nmax) n=nmax; };
void myinput()
{ cout<<"Input the array"<<endl;
for (int i=0; i<n; i++) cin>>x[i]; };
void myfun (int &first, int &ind)
{ first=-10; ind=-1;
for (int i=0; i<n; i++)
if(x[i]>0) { first=x[i]; ind=i; return; }
}; // the end of myfun
}; // the end of class
int main() { // clarray1 OBJ;
int N; cin>>N; clarray1 OBJ(N);
OBJ.myinput();
int F2,I2; OBJ.myfun( F2,I2);
if (F2!=-10)
cout<<" The first positive number= " <<F2
<< " , his index= "<<I2<<endl;
else cout<<" There are not positive numbers\n";
getch(); return 0; }
В методе MyInput массив вводим с экрана. Его можно было бы определить и другим способом, например, с помощью датчика случайных чисел.
Размерность массива в конструкторе является параметром по умолчанию. Если в головной функции записать, например, clarray OBJ(5); то в объекте будем работать с массивом размерности 5, которую передали, т. е. параметр по умолчанию 7 изменится.
Уровень B
Указания. Разработать и проверить названный в варианте класс, включив в него один или несколько конструкторов и указанные методы.
1. Класс для работы с одним целым числом:
1) вывод числа так, что между каждой десятичной цифрой должен быть один пробел;
2) вывод числа в двоичной системе счисления;
3) вывод числа в шестнадцатеричной системе счисления.
2. Класс для работы с рациональными дробями с двумя целыми полями: числитель и знаменатель. В конструкторе с двумя параметрами предусмотреть проверку знаменателя на нуль. Методы:
1)вывод дроби в виде ;
2) сокращение дроби, например, ;
3) сложение двух дробей;
4) деление двух дробей.
В методах 3) и 4) одна дробь берется из класса, а вторая передаётся через два параметра метода.
3. Класс для работы с моментами времени внутри одних суток с тремя целыми полями: час, минута и секунда. В конструкторе с тремя параметрами выполнить проверку корректности момента времени. Методы:
1) вывод момента времени, который будет через S секунд, если S передается как параметр метода. Считать, что величина S меньше количества оставшихся до конца суток секунд;
2) определение количества секунд, прошедших с начала суток:
3) вывод момента времени, который был на S секунд раньше, если S передается как параметр метода. Величина S меньше количества прошедших после начала суток секунд;
4) определение количества секунд между двумя моментами времени. Один из них берется из класса, а второй передается как три параметра метода.
Уровень С
1. Класс для работы с датами внутри одного года с двумя целыми полями: число и номер месяца. В конструкторе с двумя параметрами выполнить проверку корректности даты. Методы:
1) вывод даты следующего дня;
2) вывод даты предыдущего дня;
3) получение номера дня недели для заданной даты и вывод его названия (понедельник, вторник и т. д.), если задано, в какой день недели было первое января.
4) найти количество дней с начала года;
5) определение даты, которая будет через D дней, если D передается как параметр метода. Величина D меньше количества оставшихся до конца года дней;
6) определение количества дней между двумя датами. Одна из них берется из класса, а вторая дата передается как два параметра метода.
2. Решить задачу 1, учитывая и год, т. е. класс должен содержать три целых числа: число, номер месяца и год.
3. Класс для работы с большими целыми числами, для которых вещественный тип неприемлем, а типа int недостаточно. Для хранения такого числа предлагается использовать одномерный массив, каждый элемент которого представляет собой одну десятичную цифру числа. Во всех вариантах предусмотреть конструктор, ввод и вывод массива цифр и методы:
1) увеличение числа на единицу; вычитание двух больших чисел, т. е. вычитание двух массивов по правилу вычитания “в столбик”;
2) уменьшение числа на единицу; сложение двух больших чисел, т. е. сложение двух массивов по правилу сложения “в столбик”;
3) умножение двух больших чисел по правилу умножения “в столбик”;
4) сравнение двух больших чисел;
5), 6), 7) сложение (вычитание, умножение) большого числа (массива) с обычным целым числом. Большое число (массив) берем из класса, а обычное, представленное как число типа int, передается как параметр метода.
Указание. В методах для реализации операций над двумя большими числами один массив (число) берется из класса, а другой массив (число) передается через параметр метода.
Решить задачи главы 4, составив и используя соответствующие классы.
ОГЛАВЛЕНИЕ
Предисловие………………………………………………………………..…..3
Глава 1. Быстрое введение в язык C++….............................................4
§ 1. Основные понятия программы.…………………………….…..…….…..… .4
1.1. Пример первой программы .………………………………….….. ……..4
1.2. Директива препроцессора #include …………………………..… ……...4
1.3. Структура программы …….…………………………………………..…5
1.4. Комментарии…………………………………….………………………..6
1.5. Ключевые слова и идентификаторы, переменные………….………......6
§ 2. Ввод, вывод…………………..………….…………..…………………………7
§ 3. Выражения ……………………….……………………..…….……...………..8
3.1. Константы …………………………………………….………..….........…8
3.2. Операции и их приоритет …….…………………….…………………….9
3.3. Операции отношения и логические операции …..….………………….10
3.4. Операция присваивания …………………………..……..………………11
§ 4. Программирование ветвлений……..…………………..…….………............13
4.1. Оператор if ………………………………………………….……….........13
4.2. Тернарная операция …………..………………….………..……..............16
4.3. Оператор выбора switch ……….………….…….….……….…………....16
Упражнения и тесты …….………………..………………………….…………....19
Задачи …………………………….……………….………………..…….…...........21
Глава 2. Циклы……………………………….…............................................24
§ 1. Оператор while …………..………………………………………….………...24
§ 2. Оператор break …………..………………………………………….………...27
§ 3. Оператор do … while ..………………………………………….…….............28
§ 4. Оператор for …………..………….………………………………….……......28
Упражнения и тесты ……………………..………..……..………………………..30
Задачи …………………………….……………….………………..……………....31
Глава 3. Введение в массивы…………….…...........................................35
§ 1. Основные понятия………………..………………….………………..……....35
§ 2. Способы определения массивов…………………………………….……......38
§ 3. Вывод массивов. Функции printf и сprintf………………..………………….39
§ 4. Типы задач при работе с массивами………………………………………......42
Упражнения и тесты ……………………..………..……..……………………......45
Задачи ..………………………….……………….………………..……….………46
Глава 4. Модульное программирование. Функции…………….........48
§ 1. Функции без результатов. Параметры-значения.........................................48
1.1. Примеры. Правила оформления и вызова функций….……….……..48
1.2. Формальные и фактические параметры ………………….…………..51
1.3 Передача параметров по значению……………………………............51
§ 2. Функции типа void с результатами…………………………………….......52
2.1. Вычисление бесконечных сумм……………..……………………........52
2.2. Что такое ссылочный тип……………………..…………......................53
2.3. Параметры ссылочного типа…………………………………………...54
§ 3. Функции, отличные от void. Оператор return……………………….…….55
§ 4. Область действия имён. Локальные и глобальные имена………………..58
§ 5. Встраиваемые (inline) функции…………………………………………….60
§ 6. Параметры по умолчанию………………….……………………..….……..61
§ 7. Перегрузка функций……………………………….….……………….........62
Упражнения и тесты ……………………..………..……..……………………...63
Задачи …………………………….……………….………………..……………66