- •Аргументы функций по умолчанию.
- •Арифметические операции с указателями и с указателями на массивы.
- •5. Виртуалдық базалық класстар.(2,5) Жиындық мұра лық ету. (I 10)
- •Модификатор volatile
- •Модификатор const
- •Модификатор volatile
- •8 Виртуалдық функциялар. (2,9) Біріктірулер: синтаксисі жəне ережелері. (2,3)
Модификатор const
Константы в Си можно задавать двумя способами: с помощью директивы #define препроцессора. Например, строка
#define MILLENIUM 1000
задает символическое имя MILLENIUM для константы 1000. Препроцессор всюду в тексте заменяет это имя на константу 1000, используя текстовую подстановку. Это не очень хороший способ, поскольку при таком задании отсутствует контроль типов;
C помощью модификатора const. При описании любой переменной можно добавить модификатор типа const. Например, вместо #define можно использовать следующее описание:
const int MILLENIUM = 1000;
Модификатор const означает, что переменная MILLENIUM является константой, т.е. менять ее значение нельзя. Попытка присвоить новое значение константе приведет к ошибке компиляции
MILLENIUM = 100; // Ошибка: константу нельзя изменять
При описании указателя модификатор const, записанный до звездочки, означает, что описан указатель на константный объект, т.е. на объект, менять который нельзя или запрещено. Например, в строке
const char *p;
описан указатель на константную строку (массив символов, менять который запрещено).
Указатели на константные объекты используются в Си чрезвычайно часто. Причина состоит в том, что константный указатель позволяет прочесть объект и при этом гарантирует, что объект не будет испорчен в результате ошибки программирования, т.к. константный указатель не дает возможности изменить объект.
Модификатор volatile
Слово volatile в переводе означает "изменчивый, непостоянный". В Си к описанию переменной следует добавлять слово volatile, если ее значение может изменяться не в результате выполнения программы, а из-за каких-либо внешних событий. Например, переменная может измениться при выполнении программы-обработчика аппаратного прерывания. Другой причиной "внезапного" изменения значения переменной может быть переключение между нитями при параллельном программировании и модификация переменной в параллельной нити.
Необходимо обязательно сообщать компилятору о таких изменчивых переменных. Дело в том, что процессор выполняет все действия с регистрами, а не с элементами памяти. Оптимизирующий компилятор держит значения большинства переменных в регистрах, сводя к минимуму обращения к памяти. Непостоянная переменная может изменить свое значение в памяти, но программа будет по-прежнему использовать значение в регистре, которое осталось прежним. Из-за этого выполнение программы нарушится. Модификатор volatile запрещает даже временно помещать переменную в регистр процессора.
Пример описания переменной:
volatile int inputPort;
Здесь мы описываем целочисленную переменную inputPort и сообщаем компилятору, что ее значение может внезапно меняться в результате каких-либо внешних событий. Этим мы запрещаем компилятору помещать переменную в регистр процессора в целях оптимизации программы.
8 Виртуалдық функциялар. (2,9) Біріктірулер: синтаксисі жəне ережелері. (2,3)
Виртуальный метод (виртуальная функция) — в объектно-ориентированном программировании метод (функция) класса, который может быть переопределён в классах-наследниках так, что конкретная реализация метода для вызова будет определяться во время исполнения. Таким образом, программисту необязательно знать точный тип объекта для работы с ним через виртуальные методы: достаточно лишь знать, что объект принадлежит классу или наследнику класса, в котором метод объявлен.
Виртуальные методы — один из важнейших приёмов реализации полиморфизма. Они позволяют создавать общий код, который может работать как с объектами базового класса, так и с объектами любого его класса-наследника. При этом базовый класс определяет способ работы с объектами и любые его наследники могут предоставлять конкретную реализацию этого способа.
Базовый класс может и не предоставлять реализации виртуального метода, а только декларировать его существование. Такие методы без реализации называются «чисто виртуальными» (калька с англ. pure virtual) или абстрактными. Класс, содержащий хотя бы один такой метод, тоже будет абстрактным. Объект такого класса создать нельзя (в некоторых языках допускается, но вызов абстрактного метода приведёт к ошибке). Наследники абстрактного класса должны предоставить реализацию для всех его абстрактных методов, иначе они, в свою очередь, будут абстрактными классами.
Для каждого класса, имеющего хотя бы один виртуальный метод, создаётся таблица виртуальных методов. Каждый объект хранит указатель на таблицу своего класса. Для вызова виртуального метода используется такой механизм: из объекта берётся указатель на соответствующую таблицу виртуальных методов, а из неё, по фиксированному смещению, — указатель на реализацию метода, используемого для данного класса. При использовании множественного наследования или интерфейсов ситуация несколько усложняется за счёт того, что таблица виртуальных методов становится нелинейной.
Біріктірулер өз алдына деректер құрылымын ұсынады,с++ тіліндегі деректер қасиетіне тән программаға өзара байланысқан ақпарат бөліктерін бір айнымалыда сақтауға мүмкіндік береді.Деректерден айырмашылығы сол уақыт бірлігінде ғана ақпаратты сақтайды,басқа сөзбен айтқанда біріктіру элементіне мәнді меншіктегенлде осыған дейін меншіктелген мәнді қайта жазасыз. С++ компиляторы біріктіруді кездестірген кезде ,сол біріктірудің ең үлкен элементіне лайық жадыдан орын бөледі.
Келесі мысал
#include <iostream.h>
void main(void)
{ union distance
{ int miles; long meters; } walk;
walk.miles = 5; cout << "Пройденное расстояние в милях " << walk.miles << endl; walk.meters = 10000; cout << "Пройденное расстояние в метрах " << walk.meters << endl; }
distance біріктіруінің қолдануын көрсетеді,бірінші проггамма элементке miles мәнін меншіктейді,
9. Виртуалдық функция-мүшелер. (2,12) Біріктірулер: жай біріктірулерді құру enum-ды қодану. (2.2)
Очередная модификация базового класса приводит к неожиданным последствиям. Эта модификация состоит в изменении спецификатора функции-члена базового класса. Мы (впервые!) используем спецификатор virtual в объявлении функции. Функции, объявленные со спецификатором virtual, называются виртуальными функциями. Введение виртуальных функций в объявление базового класса (всего лишь один спецификатор) имеет столь значительные последствия для методологии объектно-ориентированного программирования, что мы лишний раз приведём модифицированное объявление класса A:
class A { public: virtual int Fun1(int); };
Один дополнительный спецификатор в объявлении функции и больше никаких (пока никаких) изменений в объявлениях производных классов. Как всегда, очень простая функция main(). В ней мы определяем указатель на объект базового класса, настраиваем его на объект производного типа, после чего по указателю мы вызываем функцию Fun1():
void main () { A *pObj; A MyA; AB MyAB; pObj = &MyA; pObj->Fun1(1); AC MyAC; pObj = &MyAC; pObj->Fun1(1); }
Если бы не спецификатор virtual, результат выполнения выражения вызова
pObj->Fun1(1);
был бы очевиден: как известно, выбор функции определяется типом указателя.
Однако спецификатор virtual меняет всё дело. Теперь выбор функции определяется типом объекта, на который настраивается указатель базового класса. Если в производном классе объявляется нестатическая функция, у которой имя, тип возвращаемого значения и список параметров совпадают с аналогичными характеристиками виртуальной функции базового класса, то в результате выполнения выражения вызова вызывается функция-член производного класса.
Сразу надо заметить, что возможность вызова функции-члена производного класса по указателю на базовый класс не означает, что появилась возможность наблюдения за объектом "сверху вниз" из указателя на объект базового класса. Невиртуальные функции-члены и данные по-прежнему недоступны. И в этом можно очень легко убедиться. Для этого достаточно попробовать сделать то, что мы уже однажды проделали - вызвать неизвестную в базовом классе функцию-член производного класса:
//pObj->Fun2(2); //pObj->AC::Fun1(2);
Результат отрицательный. Указатель, как и раньше, настроен лишь на базовый фрагмент объекта производного класса. И всё же вызов функций производного класса возможен. Когда-то, в разделах, посвящённых описанию конструкторов, нами был рассмотрен перечень регламентных действий, которые выполняются конструктором в ходе преобразования выделенного фрагмента памяти в объект класса. Среди этих мероприятий упоминалась инициализация таблиц виртуальных функций.
Наличие этих самых таблиц виртуальных функций можно попытаться обнаружить с помощью операции sizeof. Конечно, здесь всё зависит от конкретной реализации, но, по крайней мере, в версии Borland C++ объект-представитель класса, содержащего объявления виртуальных функций, занимает больше памяти, нежели объект аналогичного класса, в котором те же самые функции объявлены без спецификатора virtual.
cout << "Размеры объекта: " << sizeof(MyAC) << "…" << endl;
Так что объект производного класса приобретает дополнительный элемент - указатель на таблицу виртуальных функций. Схему такого объекта можно представить следующим образом (указатель на таблицу мы обозначим идентификатором vptr, таблицу виртуальных функций - идентификатором vtbl):
MyAC::=vptr A AC vtbl::=&AC::Fun1
На нашей новой схеме объекта указатель на таблицу (массив из одного элемента) виртуальных функций не случайно отделён от фрагмента объекта, представляющего базовый класс лишь пунктирной линией. Он находится в поле зрения этого фрагмента объекта. Благодаря доступности этого указателя оператор вызова виртуальной функции Fun1
pObj->Fun1(1);
можно представить следующим образом:
(*(pObj->vptr[0])) (pObj,1);
Біріктірулер өз алдына деректер құрылымын ұсынады,с++ тіліндегі деректер қасиетіне тән программаға өзара байланысқан ақпарат бөліктерін бір айнымалыда сақтауға мүмкіндік береді.Деректерден айырмашылығы сол уақыт бірлігінде ғана ақпаратты сақтайды,басқа сөзбен айтқанда біріктіру элементіне мәнді меншіктегенлде осыған дейін меншіктелген мәнді қайта жазасыз. С++ компиляторы біріктіруді кездестірген кезде ,сол біріктірудің ең үлкен элементіне лайық жадыдан орын бөледі.
Келесі мысал
#include <iostream.h>
void main(void)
{ union distance
{ int miles; long meters; } walk;
walk.miles = 5; cout << "Пройденное расстояние в милях " << walk.miles << endl; walk.meters = 10000; cout << "Пройденное расстояние в метрах " << walk.meters << endl; }
distance біріктіруінің қолдануын көрсетеді,бірінші проггамма элементке miles мәнін меншіктейді,
қайта санау (перечисление)-программистпен енгізілетін ақпарат,бүтін атастырылған тұрақтылар қайта санаудың мүшелері ретінде анықталуы мүмкін мысалы:
enum { RED, GREEN, BLUE };
Сонымен қатар қайта санау белгілі бір атқа ие болуы мүмкін:
enum color { RED, GREEN, BLUE };
Әрбір қайта санауымыз ол өз алдына тип ао ол әрбір қайта санау мүшесінің типі-өз алдына қайта санау
void f(color c)
{
switch(c){
case RED:
// do something
break;
case BLUE:
// do something
break;
}
}
Мысалы red- color типіне ие.
10 Виртуалдық деструкторлар. (2.7) Абстрактылық кластар(I 10) .Ағымдық шығару-енгізу класс өкілдері жəне класстар (fstream, ofstream, ifstream, ostream, istream, ios(1,11)
Деструктор әрқашанда виртуалды болып жасалады. Ол тек қана сол берілген класстын объектілерін ғана емес, оның туындыларын да орынды ,жадыға нұқсан келтірмей, жою үшін істеледі.
Деструктор – бұл берілген
класстың нақты объектілерімен байланысқан
ресурстарды босату үшін қолданылатын
арнайы функция-мүшелер. Дестукторлар
класс объектілерін автоматты түрде
бұзады (разрушение). Форматы:
class className
{ public:
className(); // үнсіздік бойынша
конструктор
// басқа конструкторлар
~className(); // деструкторды
хабарлау
// басқа функция-мүшелер
};
Деструкторлар қасиеттері
Деструктор – объектіні жою кезінде
шақырылатын әдіс
Аты класс атымен сәйкес келеді, ~
белгісінен басталады
Параметрлері жоқ
class Complex
{ …
public:
~Complex()
{
}}
Деструкторда файлдарды жабуға және
жадыны босатуға болады.
Қосымша
new операторы
конструкторды, ал delete
– деструкторды шақырады
Динамикалық жадыны қолдану,
яғни жады компиляция кезінде емес,
программаны қосу кезінде анықталады.
new опрератоы
барлық мәліметтер типтері үшін жұмыс
істейді
Модификатор const
-
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
class Shape
{
public:
virtual double area() const = 0;
};
class Square: public Shape
{
public:
Square(double a = 1): _a(a) { }
virtual double area() const { return _a * _a; }
private:
double _a;
};
class Circle: public Shape
{
public:
Circle(double r = 1): _r(r) { }
virtual double area() const { return 3.14159265358 * _r * _r; }
private:
double _r;
};
int main()
{
Shape *shapes[5];
shapes[0] = new Circle (3);
shapes[1] = new Square (2);
shapes[2] = new Square (2.5);
shapes[3] = new Circle (5);
shapes[4] = new Circle (10);
for (int i = 0; i < 5; ++i)
std::cout << shapes[i]->area() << std::endl;
for (int i = 0; i < 5; ++i)
delete shapes[i];
return 0;
}
Байқағанымыздай Shape абстрактный себебі бұл жерде виртуалдық функция area бар. Класстардағы Shape - Circle и Square де area анықтамасы бар. Басты функциядағы Shape де массив құрылған.Кейн оз фигурасын анықтайды.Цикл ішінде қандай ұяшықта қандай массив орналасканын білгеннен кейін әрқайсысына area шақырамыз,ол әр обьектіге оз қадамын жасайды.
В языке С++ ввод/вывод описывается как набор классов, описанный в заголовочном файле iostream.h. Аналогами потоков stdin, stdout, stderr являются классы cin, cout и cerr. Эти три потока открываются автоматически. Поток cin связан с клавиатурой, а cout, cerr - с дисплеем. Классы iostream используют перегруженные операции “<<” для занесения (записи) в поток и операции “>>” для извлечения (чтения) из потока. Обе операции возвращают ссылку на тип iostream, что позволяет объединять в одной строке несколько потоковых операций.Существуют перегруженные операции для всех типов данных (в том числе и для пользовательских), тем самым необходимость проверки соответствия типов отпадает. Компилятор самостоятельно выбирает нужную операцию в соответствии с типом данных:
int ivalue; float fvalue; char c; cin>>ivalue>>fvalue>>c; cout<<”integer: “<<ivalue<<endl<<” float: “<<fvalue<<”char: “<<c;
Для вставки символа перевода строки необходимо либо выводить символ ‘\n’, либо константу endl. Для форматирования выводимых данных для объектов iostream включены следующие методы: precision(int p) - количество знаков после запятой, width(int w) - размер поля для вывода, setf(long manip) - установка флагов, определяющих формат вывода(ios::dec, ios::oct, ios::hex, ios::fixed, ios::scientific, ios::left, ios::right). Классы iostream. Все объекты ввода/вывода, описанные в библиотеке iostream, используют оди и тот же базовый класс ios (за исключением классов буферизованных потоков). Эти производные классы делятся на 4 категории.
Классы потокового ввода |
|
istream |
Универсальный класс ввода, или родительский класс для других производных потоковых классов ввода. |
ifstream |
Ввод из файлов. |
istream_withassign |
Ввод из потока cin. |
istrstream |
Ввод из строки. |
Классы потокового вывода |
|
ostream |
Универсальный класс вывода, или родительский класс для других производных потоковых классов вывода. |
ofstream |
Вывод в файлы. |
ostream_withassign |
Вывод в потоки cout, cerr и clog. |
ostrstream |
Вывод в строку. |
Классы потокового ввода/вывода |
|
iostream |
Универсальный класс ввода/вывода, или родительский класс для других производных потоковых классов ввода/вывода. |
fstream |
Ввод/вывод в файлы. |
stdiostream |
Стандартный поток ввода/вывода. |
strstream |
Ввод/вывод в строку. |
Классы буферизованных потоков |
|
streambuf |
Родительский класс для производных буферизованных классов. |
filebuf |
Буферизованный поток для файлов. |
stdiobuf |
Буферизованный поток для стандартного файлового ввода/вывода. |
strstreambuf |
Буферизованный поток для строк. |
Все классы, производные от ios использут объект класса streambuf. Потоковый ввод-вывод. В следующем примере используется класс ifsteam :
#include <fstream.h> void main() { char one_line[80]; //объявление потока, дескриптора файла
ifstream my_input_stream(“iputfile.cpp”,ios::in); while(my_input_stream) { // ввод строки из потока my_input_stream.getline(one_line,sizeof(one_line),’*’);
cout<<endl<<one_line;
} //закрытие потока my_input_stream.close();
}
Для объекта my_input_stream можно использовать методы open(), rdbuf() из класса ifstream и ряд методов из родительского класса istream: gcount(),get(), getline(), ignore(), peek(), putback(), read(), seekg(), tellg(). При создании объекта вызывается конструктор, которому передается имя файла, а также один или несколько режимов ввода, задаваемые константами, предопределенными в классе ios (ios::in ios::binary ios::nocreate). Константы объединяются с помощью побитовой операции ИЛИ “|”. Значение дескриптора файла можно использовать в логических проверках. При достижении конца файла значение устанавливается в ноль (условие EOF). Метод getline() читает текстовые строки из входного потока ( знак “*” является разделителем строк. Приме програмы, использующией ofstream:
#include <fstream.h> #include <string.h> void main() { int i=0; long ltellp;
char sample[40]=”Sample string\n”; // объявление потока
ofstream my_out_stream(“outfile.out”,ios::out); while(sample[i]!=0) { //вывод символа my_out_stream.put(sample[i]); //определение позиции вывода
ltellp=my_out_stream.tellp(); cout<<”\ntellp value: “<<ltellp; i++;
} my_out_stream.write(sample,strlen(sample)); ltellp.my_out_stream.tellp(); cout<<”\ntellp after write: “<<ltellp; my_out_stream.close();
}
Обратите внимание на то, что при выполнении этой программы производится преобразование символа “\n” в два символа в файле. Допустимо использование методов open(), rdbuf() из класса ofstream и flush(), put(), seekp(), tellp(), write() из класса ostream.
11 Динамикалық жады. Сілтемелер жəне массивтер. Сілтемелік тип. (2.13) Айнымалылық сілтемелерді хабарлау Сілтемелері бар жай операторлар. (1.12)
Егер объектінің көлемі немесе массивтің размері алдын-ала белгісіз болса (мысалы, сурет), немесе объектінің размері оның ішкі функциясын құруға тым үлкен болса, онда С++ тілінде динамикалық жадыны қолданамыз.
Ол үшін біз екі түрлі операторды білуіміз қажет:
new- жадыны бөлу, егер жадыны бөлу жүзеге аспаса, онда нөлдік мән (указатель) қайтарылады
delete- жадыны босату, жады босағаннан кейін барлық компиляторда нөлдік мән (нулевой указатель) қайтарыла бермейді.
#include <iostream>
using namespace std;
int main() {
int* obj = new int(45);
cout<<"*obj="<<*obj<<endl;
delete obj;
int* array = new int[10];
cout<<"array:";
for(unsigned int i=0; i<10; i++)
cout<<array[i]<<" ";
cout<<endl;
delete [] array;
array=0; }
Сілтемелер С++ тілінің бірден-бір ерекшелігі болып табылады. Оларды қолдану программаның тез жұмыс істеуіне және жадыны тиімді қолдануға алып келеді. Мысалы, көп жағдайда функцияның аргументінің орнына объектіге сілтеме қолданған дұрыс.Егер бқл сілтемені қолданбаса, онда әрқашан оның көшірмесін құруға тура келеді.
Сілтемелермен төмендегі операциялар орындалады:
& - айнымалының адресін алу
* - объектіге доступ алу
-> - структураның мүшелеріне доступ алу
[] – индексация, массивтің элементтеріне доступ алу, бұл кезде сілтеме нөлдік элементтің адресін иеленеді.
Индексация операциясы адрестік арифметика арқылы жүзеге асады. Адрестік арифметика дегеніміз ++,-- және +,-.
Сілтемелерді кез келген типке құра аламыз, ссылка мен биттік полядан басқа. Сілтемелер тек берілгенге ғана емес, сонымен қатар функцияларға да сілтеме жасай аламыз.
Массив- бір типті көп объектілердің жадыда бірінен кейін бірі , яғни последовательно орналасуы. Индексация- массивтің элементтеріне операция жасауға доступ алу, бұл кезде элементтер нөлден бастап номерленеді.
Ішкі массивтер сілтемелер сияқты нөлдік элементке жүзеге асады. Екеуінің айырмашылығы массив үшін бүкіл массивтің размерін қайтаратын sizeof() операторының қолданылуы. Массивтің размері тек тұрақты түрде сілтеменелуі мүмкін.
#include <iostream>
using namespace std; // массивы по 10 элементов типа int
int array1[10]; // не инициализированный массив
int array2[]= {1,2,3,4,5,6,7,8,9,0};//инициализированный массив (заданы значения каждого элемента)
// объявления двухмерных массивов
int array3[5][6];
int array4[2][3]={ {0,1,2}, {2,1,0} };
// строки в С завершаются 0, более подробно см. строки
char* str1="Hello, world"; // указатель на строку "Hello, world"
char str2[]="Hello, world"; // массив символов
char str3[]={'H','e','l','l','o',',',' ','w','o','r','l','d','0' };
int * iptrarray[10]; // массив указателей типа int
int (*iarrayptr)[10]; // указатель на массив из 10 элементов типа int
int main()
{int* iptr;
cout<<"sizeof(iptr)=" <<sizeof(iptr) <<endl;
cout<<"sizeof(array1)="<<sizeof(array1)<<endl;
cout<<"array2[11]="<<array2[11]<<endl;
return 0;}
Сілтемелік тип сілтемелер сияқты объектінің адресін сақтайды, бірақ автоматты түрде объектінің өзіне, яғни негізі бұлар сілтеме жасалып тұрған айнымалылардың синонимдері болып келеді. Сілтеме жасалып тұрған айнымалыны анықтауда ьасында берілген мән міндетті түрде болуы керек. Төмендегі мысалдан сілтемелерге қарағанда сілтемелік типті қолдану шамалы ыңғайлы екенін байқауға болады.
#include <iostream>
using namespace std;
void swap_ref(int &a, int &b) { int c=a; a=b; b=c; }
void swap_ptr(int *a, int *b){ int c=*a; *a=*b; *b=c; }
int main(){int A=10,B=20;
cout << "A="<<A<<" B="<<B<<endl;
swap_ref(A,B);
cout << "after swap_ref(A,B): A="<<A<<" B="<<B<<endl;
swap_ptr(&A,&B);
cout << "after swap_ptr(&A,&B): A="<<A<<" B="<<B<<endl;
return 0;}
Указатель — это переменная специального типа. Она хранит не какое-то числовое значение, а адрес (номер первого байта в памяти компьютера), по которому хранится какая-то другая переменная. При создании указателя необходимо задать тип переменной, на которую он указывает. Синтаксис объявления указателя такой:
имя_типа * идентификатор;
Пример:
int * pi;
float * pf, f;
double * ps, * pt;
В первой строке этого примера объявлены переменная pi, являющейся указателем на тип int (то есть в ячейке памяти, на которую указывает pi должна хранится переменная типа int). Во второй строке объявлены переменная pf, являющейся указателем на тип float и переменная f типа float. Обратите особое внимание на эту строчку: для того, чтобы объявить несколько указателей в одной строке, необходимо перед идентификатором каждого из них поставить символ *. А еще лучше объявлять в одной строке только одну переменную. В третей строке объявляется два указателя на тип double: ps и pt.
Указателям можно присваивать значение, являющееся указателем того же типа (которое может быть результатом оператора new, оператора & или другим указателем того же типа). К указателям можно применять оператор разыменования *. Кроме этого с указателями можно выполнять ряд других операций. Далее мы предполагаем, что p и q — указатели одного типа, например, объявленные как int *p, *q.
Как и числа, указатели можно сравнивать между собой.
p==q
Проверка двух указателей на равенство (то есть указывают ли они на одну и ту же ячейку памяти)
p!=q
Проверка на неравенство
p<q
Возвращает true, если ячейка, на которую указывает p находится в памяти раньше, чем ячейка, на которую указывает q. Аналогично определяются сравнения p<=q, p>q, p>=q
12Глобалды айнымалыларға, сондай аты бар жасырын локалдық айнымалыларға рұқсат (оператор ::).(2.12)
С++ тілінде Глобалды айнымалыларға, сондай аты бар жасырын локалдық айнымалыларға рұқсат қарастырылған.Көріну облысының рұқсат ету операциясы мұндай кезде глобальды айнымалыны пайдалануды рұқсат етеді:мысалы
int x=5; void main() { int x=3; printf("Локальная x= %d\n",x); printf("Глобальная x= %d\n",:x); }
программа жазу кезінде мына модификаторлар қолданылады:*-сілтеуші,()-функция,[]-массив,бірақ бұл модификаторлардын шектен тыс жазу қиындық туындату мүмкін Айнымалылардың дұрыс интерпритациясы үшін келесі ережелер қарастырылады:
1)модификатор бағытталған обьектіге неғұрлым жақын болса,соғұрлым оның мүмкіндігі жоғары
2){},[] -*-ге қарағанда мүмкіндігі жоғары
3)сілтеушінің () жақшаларды пайдалану арқылы мүмкіндігін арттыруға болады
Рұқсат берудің модификаторлары-volatile ,const компиляторға бағытталған обьектінің тұрақсыз ,тұрақтылығын анықтайды.Егер айнымалы const ретінде жарияланатын болса,онда ол статикалық айнымалылар секілді проектінің басқа модульінде қолжетімсіз және программаның орындалу кезінде өзгере алмайды.Const модификаторы арқылы #define директивасы арқылы анықталатын тұрақтылар пайда болады.
Например: const double PI=3.141528; const char yes='Y';
Файл сілтеме арқылы жіберілу кезінде айнымалыларды const деп жариялаудың маңызы -функция параметрлерін модификациядан қорғау.Модификатор volatile обьектінің мәнін компилятордан жасыра алады .компьютердің жүйелік уақытын көрсететін глобальдік айнымалыны тоқтатуды өңдеу кезінде ол озінің мәнін өзгерту мүмкін. Компилятор регистрлік жадыға ондай айнымалыларды орналастырмауы керек
Мысалы: volatile unsigned timer;
13 Доступ к членам базовых классов внутри производного класса. if операторы. if-else операторы. Салынатын if-else операторлар. if-else-if операторы.(1.5)
Объект производного класса фактически построен из нескольких частей. Каждый базовый класс вносит свою долю в виде подобъекта, составленного из нестатических данных-членов этого класса. Объект производного класса построен из подобъектов, соответствующих каждому из его базовых, а также из части, включающей нестатические члены самого производного класса. Так, наш объект NameQuery состоит из подобъекта Query, содержащего члены _loc и _solution, и части, принадлежащей NameQuery, - она содержит только член _name.
Внутри производного класса к членам, унаследованным из базового, можно обращаться напрямую, как к его собственным. (Глубина цепочки наследования не увеличивает затраты времени и не лимитирует доступ к ним.) Например:
void
NameQuery::
display_partial_solution( ostream &os )
{
os < < _name
< < " is found in "
< < (_solution ? _solution->size() : 0)
< < " lines of text\n";
}
Это касается и доступа к унаследованным функциям-членам базового класса: мы вызываем их так, как если бы они были членами производного - либо через его объект:
NameQuery nq( "Frost" );
// вызывается NameQuery::eval()
nq.eval();
// вызывается Query::display()
nq.display();
либо непосредственно из тела другой (или той же самой) функции-члена:
void
NameQuery::
match_count()
{
if ( ! _solution )
// вызывается Query::_vec2set()
_solution = _vec2set( &_loc );
return _solution->size();
}
If құрылымы жалғыз таңдауы бар құрылым деп аталады, өйткені онда
бір әрекет таңдалады немесе өткізіледі. If/else құрылымы екі таңдауы бар құрылым деп аталады, өйткені онда екі альтернативті әрекеттің арасында 15 таңдау орындалады
If таңдау құрылымының жалпы түрі:
Іf (өрнек) оператор1;
If/else құрылымының жалпы түрі:
Іf (өрнек) оператор1;
Else оператор2;
Егер өрнек мәні ақиқат болса (нөлден өзгеше болса), онда оператор1
орындалады, керісінше жағдайда оператор2 орындалады.
Си тілінде If/else құрылымымен тығыз байланысқан (?:) шартты
операциясы қарастырылған. Бұл операторға үш операнд қажетті. Шартты операциясымен бірге операндтар шартты өрнекті құрады. Бірінші операциялык шарт болып табылады, екінші операнд барлық шартты өрнектің мәні болады, егер шарт ақиқат болса және үшінші операнд барлық шартты өрнектің мәні болады, егер шарт жалған болса.
Мысалы,
x>y ? printf(“max=%d\n”, x) : printf(“max=%d\n”, y);
If/else бірінің ішіне бірі енген құрылымдар құрамды шарттарды тексеру
үшін пайдаланылады, сондай-ақ If/else бір құрылымдар If/else басқа
құрылымдардың ішіне орналастырылады.
Келесідей жазылу түрін келтірейік:
Іf (өрнек1) оператор1;
Else іf (өрнек2) оператор2;
Else оператор3;
Егер өрнек1 ақиқат болса, онда оператор1 орындалады. Егер өрнек1
жалған болып, өрнек2 ақиқат болса, онда оператор2 орындалады. Екі өрнек
те жалған болған кезде оператор3 орындалады.
Келесі программада екі таңдауы бар if/else құрылымының қолдану
мысалы келтірілген.
// Жеңілдік есебімен сатып алу бағасы есептеледі
#include <stdio.h>
#include <conio.h>
void main()
{
float sum; /* сатып алудың бағасы */
printf(“\n Жеңілдік есебімен сатып алу бағасы ”);
printf(“есептеледі \n”);
printf(“Сатып алу бағасын енгіз ->”);
scanf(“%f”, &sum);
if (sum<1000) printf(“Жеңілдік берілмейді.\n”);
else { printf(“Сізге жеңілдік беріледі ”);
if (sum>3000) { printf(“5%\n”);
sum = 0.95 * sum; }
else { printf(“3%\n”);
sum = 0.97 * sum; };
printf(“Жеңілдікпен берілетін бағасы %.2f тг\n”, sum); }
printf(“\nАяқталу үшін <Enter> басыңыз”);
getch(); }
Программаның нәтижесі:
Жеңілдік есебімен сатып алу бағасы есептеледі
Сатып алу бағасын енгіз -> 2250
Сізге жеңілдік беріледі 3%.
Жеңілдікпен берілетін бағасы: 2182.50 тг
14 . Доступ к элементам массива. Вычисление размера массива. Многомерные
массивы. Приведите пример Жадыны динамикалық тарату операторлары (new, delete).(2.7)
Объявление дружественного класса позволяет всем его методам получить доступ ко всем переменным и методам другого класса.
Дружественный класс или член класса будет доступен только в том случае, если он был объявлен в области видимости самого класса или ранее во внешней области видимости, внутри которой располагается область видимости, содержащая объявление класса с объявлениями друзей класса.
К друзьям и дружественности применимы следующие правила:
• на описания friend не влияют спецификаторы public,
protected или private;
• описания friend не взаимны: если А объявляет В другом, то это не означает, что А является другом для В;
• дружественность не наследуется: если А объявляет В другом, классы, производные от В, не будут автоматически получать доступ к элементам А;
• дружественность не является переходным свойством: если А объявляет В другом, классы, производные от А, не будут автоматически признавать дружественность В.
Обычное объявление функции-члена гарантирует три логически разные вещи:
• во-первых, функция имеет право доступа к закрытой части объявления класса;
• во-вторых, функция находится в области видимости класса;
• в-третьих, функция должна вызываться для объекта класса, то есть имеется указатель this,
Объявив функцию-член как static, мы придаем ей только первые два свойства. Объявив функцию как friend, мы наделяем ее только первым свойством.
Так же как и функции-члены, функции-друзья явно указываются в объявлении класса, друзьями которого они являются. Поэтому они в той же мере являются частью интерфейса класса, в какой ею являются функции-члены.
Так же как и объявление члена, объявление friend не добавляет новое имя в охватывающую область видимости
Класс-друг должен быть предварительно объявлен в охватывающей области видимости или определен в области видимости, непосредственно охватывающей класс, объявивший его другом. При этом не принимаются во внимание области видимости вне области видимости самого внутреннего охватывающего пространства имен.
Функцию-друга можно явно объявить точно так же, как и класс-друг, или ее поиск осуществляется по типам ее аргументов так, как будто она была объявлена вне класса, но в области видимости, непосредственно охватывающей класс.
Из этого следует, что функция-друг класса должна быть либо явно объявлена в охватывающей области видимости, либо иметь аргументы этого класса. В противном случае функцию-друга вызывать нельзя.
Оператор C++ new позволяет вашим программам распределять память во время выполнения. Для использования оператора new вам необходимо указать количество байтов памяти, которое требуется программе. Предположим, например, что вашей программе необходим 50-байтный массив. Используя оператор new, вы можете заказать эту память, как показано ниже:
char *buffer = new char[50];
Говоря кратко, если оператор new успешно выделяет память, он возвращает указатель на начало области этой памяти. В данном случае, поскольку программа распределяет память для хранения массива символов, она присваивает возвращаемый указатель переменной, определенной как указатель на тип char. Если оператор new не может выделить запрашиваемый вами объем памяти, он возвратит NULL-указатель, который содержит значение 0. Каждый раз, когда ваши программы динамически распределяют память с использованием оператора new, они должны проверять возвращаемое оператором new значение, чтобы определить, не равно ли оно NULL.
Например, следующая программа использует оператор new для получения указателя на 100-байтный массив:
#include <iostream.h>
void main(void)
{ char *pointer = new char[100]; if (pointer != NULL) cout << "Память успешно выделена" << endl; else cout << "Ошибка выделения памяти" << endl; }
Для освобождения памяти с использованием оператора delete вы просто указываете этому оператору указатель на данную область памяти, как показано ниже:
delete pointer;
Следующая программа использует оператор delete для освобождения выделенной с помощью оператора new памяти:
#include <iostream.h>
#include <string.h>
void main(void)
{ char *pointer = new char[100]; strcpy(pointer, "Учимся программировать на языке C++"); cout << pointer << endl; delete pointer; }
По умолчанию, если ваша программа не освобождает выделенную ей память до своего завершения, операционная система автоматически освобождает эту память после завершения программы. Однако если ваша программа использует оператор delete для освобождения памяти по мере того, как она (память) становится ненужной, то эта память вновь становится доступной для других целей (возможно, для вашей программы, которая опять будет использовать оператор new, или для операционной системы).
15 2.Достық класстар жəне функциялар.(1.2) Жадыны динамикалық тарату операторлары (new, delete).(2.7)
Достық класстар мен функциялар
Программадағы класс элементтеріне және басқа класстарға қатынау шектелген. Біз тек қана public кілттік сөзінен кейін анықталған немесе сипатталған класс элементтеріне тікелей қатынай аламыз. Бірақ кейбір жағдайда, әдістері private-те және protect-те жарияланған элементтерді қоса алғанда класстың барлық элементтеріне қатынай алатын,класстан тыс функцияны немесе басқа классты анықтау талап етіледі.
Достық функциялар
Біз С++те friend кілттік сөзін пайдаланып класс үшін достық функция деп аталатын функцияны анықтауға болады. Класста тек достық функцияны хабарлауы ғана тұрады. Оны анықтау класстан тыс орналасады. Достық функцияны класстың кез келген секциясында (public, private, protect) жариялауға болады.
Достық функция класс элементі емес, бірақ ол класстың барлық элементтеріне сонымен қоса private және protect-ке де қатынай алады. Бір ғана функция екі немесе одан да көп класс үшін достық болуы мүмін.
Келесі мысалда Point класс үшін достық функция болатын Clear функциясы анықталған.
Clear достық функциясы private ретінде жарияланған m_x және m_y деректер элементтерінің мәндерін өзгерті үшін қолданылады.
// Класс point
class point
{
public:
friend void point::Clear(point*);
// Интерфейс класса…
private:
int m_x;
int m_y;
};
// Функция Clear
void Clear(point* ptrPoint)
{
// Обращаемся к элементам класса, объявленным как private
ptrPoint->m_x = 0;
ptrPoint->m_y = 0;
return;
}
// Главная функция
void main()
{
point pointTestPoint;
Clear(&pointTestPoint);
}
Friend кілттік сөз көмегімен бір класстың қайсыбір әдістерін екінші бір класс үшін достық деп жариялауға болады. Мұндай әдістер өздері басқа классқа кірсе де, класстың барлық элементтеріне, тіпті private және protect те жарияланса да, қатынай алады.
Келесі мысалда Line және Point екі классын анықтайық. Point классында Set әдісін анықтап, оны Line классында достық ретінде жариялаймыз. Set достық әдісі Line класының барлық элементіне қатынай алады.
class line;
// Класс point
class point
{
public:
// Метод Set класса point
void Set(line*);
};
// Класс line
class line
{
public:
friend void point::Set(line*);
private:
int begin_x, begin_y;
int end_x, end_y;
};
// Функция Clear
void point::Set(line* ptrLine)
{
ptrLine->begin_x = 0;
ptrLine->begin_y = 0;
// …
return;
}
void main()
{
point pointTestPoint;
line lineTestPoint;
pointTestPoint.Set(&lineTestPoint);
}
Достық класстар.
Достық функциялар мен әдістер сияқты достық классты да жариялауға болады. Достық класстағы барлық әдістер класстың барлық элементтеріне, private және protect-те жарияланған элементтерді қоса есептегенде, қатынай алады.
Алдыңғы мысалда Point классы Line классына достық класс деп анықтауға болар еді. Point классынаң барлық әдістері Line классының кез келген элементтеріне қатынай алады.
// Класс point
class point
{
};
class line
{
public:
friend class point;
};
Оператор C++ new позволяет вашим программам распределять память во время выполнения. Для использования оператора new вам необходимо указать количество байтов памяти, которое требуется программе. Предположим, например, что вашей программе необходим 50-байтный массив. Используя оператор new, вы можете заказать эту память, как показано ниже:
char *buffer = new char[50];
Говоря кратко, если оператор new успешно выделяет память, он возвращает указатель на начало области этой памяти. В данном случае, поскольку программа распределяет память для хранения массива символов, она присваивает возвращаемый указатель переменной, определенной как указатель на тип char. Если оператор new не может выделить запрашиваемый вами объем памяти, он возвратит NULL-указатель, который содержит значение 0. Каждый раз, когда ваши программы динамически распределяют память с использованием оператора new, они должны проверять возвращаемое оператором new значение, чтобы определить, не равно ли оно NULL.
Например, следующая программа использует оператор new для получения указателя на 100-байтный массив:
#include <iostream.h>
void main(void)
{ char *pointer = new char[100]; if (pointer != NULL) cout << "Память успешно выделена" << endl; else cout << "Ошибка выделения памяти" << endl; }
Для освобождения памяти с использованием оператора delete вы просто указываете этому оператору указатель на данную область памяти, как показано ниже:
delete pointer;
Следующая программа использует оператор delete для освобождения выделенной с помощью оператора new памяти:
#include <iostream.h>
#include <string.h>
void main(void)
{ char *pointer = new char[100]; strcpy(pointer, "Учимся программировать на языке C++"); cout << pointer << endl; delete pointer; }
По умолчанию, если ваша программа не освобождает выделенную ей память до своего завершения, операционная система автоматически освобождает эту память после завершения программы. Однако если ваша программа использует оператор delete для освобождения памяти по мере того, как она (память) становится ненужной, то эта память вновь становится доступной для других целей (возможно, для вашей программы, которая опять будет использовать оператор new, или для операционной системы).
