Бублик Володимир Васильович Програмування - 2 Лекція 7. Процедурне програмування. Області видимості в програмі Лекції для студентів 2 курсу
Види імен
Імена об'єктів
Імена типів (структур, класів )
Імена функцій
Кожне ім'я має унікальну інтерпретацію в межах своєї області видимості, яка може складатися з кількох файлів, кожен з яких може компілюватися окремо
Для забезпечення можливості осібної компіляції до файлу з текстом програми (.cpp) додаються заголовні файли (.h) , в яких пояснюються (оголошуються) імена, необхідні для компіляції цього файлу
Визначення і оголошення
Визначення приводить до створення об'єкту Визначення розміщують в .cpp файлах На підставі визначення будується об'єктний код побудови об'єкту
Оголошення пояснює існуючий об'єкт Оголошення розміщують в .h файлах Оголошення служать для побудови об'єктного коду, що використовує об'єкт
Визначення функції root.cpp
double root ( double x, double eps)
{
double s=0.5*x;
double t;
do
{
t=s;
s=(s+x/s)*0.5;
}
while ((fabs(s-t)/s)>eps);
return s;
};
Оголошення функції root
// root .h
double root ( double x, double eps);
Використання функції root
int main ( int argc, char * argv[])
{
const double two=2.0;
const double prec = 0.000001;
cout.precision(17);
// Де взяти інформацію про типи параметрів і результату?
cout << root (two, prec)<<endl;
return 0;
}
Використання функції root
# include "root.h“
int main ( int argc, char * argv[])
{
const double two=2.0;
const double prec = 0.000001;
cout.precision(17);
// З файлу root.h
cout << root (two, prec)<<endl;
return 0;
}
Омоніми
Чи може ім'я root використовуватися в програмі за іншим призначенням?
Так, в іншій області видимості
Видимість
Глобальна область видимості (вся програма)
Простори імен
Локальна область видимості
блок,
функція,
файл,
клас ( з'явиться пізніше)
Глобальна область видимості
Глобальна область видимості об'єкти, функції і типи, визначені за межами блоків будь-якого типу
Глобальні визначення
//unit1.cpp
int g_Var = 20; // Визначення глобальної змінної
void f( void ) //Визначення функції
{
cout<<"I am f () from unit1 and I see g_Var=";
cout<<g_Var<<endl;
return ;
}
Функція void f( void ) бачить змінну g_Var , оскільки вони визначені в одному файлі
Доступ до глобальних імен і функцій
Як відкомпілювати програму, що використовує глобальні об'єкти з іншого файлу? Як дати функції int main() можливість побачити змінну g_Var ?
//unit2.cpp
int main ()
{
cout<<g_Var<<endl; //Як використати змінну
f(); // g_Var з unit1.cpp?
return 0;
}
Заголовний файл
Для розповсюдження інформації про об'єкти, визначені у файлі unit1.cpp , створюємо заголовний файл
//unit1.h
// Оголошення глобальних об'єктів
void f( void );
extern int g_ Var ;
Оголошення функції відрізняється від визначення відсутністю її тіла.
Щоб відрізнити оголошення змінної від її визначення додають ключове слово extern
Використання оголошень
//unit2.cpp
# include “unit1.h"
// #include “unit1.cpp“
// чому не коректно приєднувати текст .cpp файлу?
int main ()
{
cout<<g_Var<<endl; //Використано оголошення
f(); //з unit1.h
return 0;
}
extern int g_ Var ; void f(void);
Глобальні типи
Де розміщувати визначення структур?
struct Point
{
double _x, _y;
};
Якщо безпосередньо в .cpp файлі, то як дати доступ до цього визначення з різних файлів?
Якщо в заголовному файлі .h , то як усунути повторні визначення при приєднанні одного заголовного файлу до різних .cpp файлів?
Захист заголовного файлу
Заголовні файли захищають від повторного включення в текст програми при одночасній компіляції багатьох файлів
//File Point.h
#ifndef _POINT_H_
#define _POINT_H_
struct Point
{
double _x, _y;
};
#endif
Оголошення типів
Чи всі програмні файли потребують визначення структури і відповідного #include ? ― Ні, часом досить лише оголошення структури
Часто #include в іншому заголовному файлі недоречний ( доробка на майбутнє )
//Some.h
struct Point; // оголошення структури
// досить для указника без ініціалізації
Point * p;
//досить для оголошення функції без виклику
Point sum (Point a, Point b);
Простір імен. Оголошення
Простір імен дає можливість зібрати пов'язані між собою позначення в одному місці та відрізняти їх від інших імен з глобального контексту
//File.h
void f( void );
namespace myNames
{
void f( void );
void g ( void );
}
Тут присутні дві різні функції void f( void )
Визначення елементів простору імен
//File.cpp
void f( void )
{
cout<<"I am global";
return ;
}
void myNames:: f(void)
{
cout<<"I am f from myNames";
return ;
}
Операція розв'язання області видимості
//Source.cpp
# include "File.h "
int main ()
{
f(); //взяти f з глобального контексту
myNames :: f(); //взяти f з простору myNames
myNames :: g(); //взяти g з простору myNames
return 0;
}
Приєднати простір імен
//Source.cpp
# include "File.h "
using namespace myNames;
int main ()
{
:: f(); //взяти f з глобального контексту
myNames :: f(); //взяти f з простору myNames
g(); //взяти g з простору myNames
return 0;
}
Стандартний простір імен std
Стандартний простір імен містить визначення імен і типів
Наприклад,
cin, с out , endl
istream , ostream
Їх можна вживати через операцію розв'язання області дії
std:: с out , std:: endl
std:: ostream , std:: istream
Наприклад,
std::cout<<"x="<<x<<std::endl;
Можна приєднати простір імен
using namespace std;
Локальна область видимості
Кожна функція і блок визначають свою власну локальну область видимості. Вона складається з області видимості, в якій знаходиться блок чи визначення функції, доповненої власним простором імен.
Деякі імена з ширшого контексту при цьому можуть затінятися локальними іменами.
Погодження. Локальні області видимості не можуть містити визначень функцій , але можуть містити визначення типів даних
Розв'язання глобальної області видимості
i nt x = 1;
int main ()
{
int x = 2;
cout<<"x="<<x<<endl; //локальна х
cout<<"::x="<< ::x <<endl; //глобальна х
return 0;
}
Локальна змінна затіняє однойменну змінну з ширшого контексту. Розв'язання (доступ до глобальної) можливе
Глобальні об'єкти
Рекомендується уникати глобальних об'єктів (чому?), але глобальні сталі абсолютно правомірні
Визначення
//some.cpp
const float pi = 3.14159;
Оголошення
//some.h
extern const float pi;
Вкладені блоки
Область видимості блоку включає глобальний контекст, в якому цей блок знаходиться, доповнений локальними об'єктами з блоку
Чим вужча область видимості кожного об'єкту, тим краще
Область видимості функції включає глобальний контекст ( краще, щоб його не було), в якому це визначення знаходиться, доповнений параметрами та локальними об'єктами з тіла функції
const int n=10 ;
double prod( double x[], double y[] )
{ //{ n } + { x, y, sum} + {i}
double sum = 0;
for ( int i=0; i<n; i++)
sum += x[i]*y[i];
return sum;
}
Файл як область видимості
Можна обмежити видимість об'єктів .cpp файлом, у якому вони визначені. Такі об'єкти називають статичними
//some.cpp
static unsigned currentID = 0;
void increaseID()
{ ++currentID; return ;}
int getFreeID()
{ increaseID(); return currentID; }
Персистентність
Персистентність визначає тривалість життя об'єктів від моменту зв'язування до моменту видалення
Зв'язування об'єкту з його значенням
раннє (статичне)
пізнє (динамічне)
Статичне зв'язування
При складанні програми
константи препроцесора (уникаємо їх – чому?) # define PI 3.14159
ім'я функції з її тілом void f() { cout <<« I am f()"<<endl; return; }
При компіляції
іменовані константи const float pi = 3.14159;
глобальні і статичні змінні static unsigned int currentID = 0;
Динамічне зв'язування
Після входу в блок { double z=x; x=y; y=z;}
При виклику функції u = root (v, e); // double x = v , double eps = e
При виділенні пам'яті int * p = new int (1);
Тривалість життя
Локальні об'єкти живуть до кінця виконання блоку чи функції (в стеку)
Створені програмою (за допомогою new ) об'єкти живуть, доки їм дозволяє програма ( видалення за допомогою delete) (в купі , in heap)
Глобальні і статичні об'єкти живуть до кінця виконання програми
Постійні об'єкти можуть зберігатися у файлах (базах даних) і жити вічно
Призначення об'єктів
Кожній змінній ― одне призначення
// Багатоцільові змінні ознака кепського тону!
temp = sqrt(b*b – 4*a*c)
root[0] = (-b -temp)/(2*a);
root[1] = (-b+temp)/(2*a);
…………………… .
temp = root[0];
root[0] = root[1];
rout[1] = temp;
Призначення об'єктів
Кожній змінній ― одне призначення
// Добре
double discriminant = sqrt(b*b – 4*a*c)
root[0] = (-b - discriminant )/(2*a);
root[1] = (-b+ discriminant )/(2*a);
…………………… .
double replacer = root[0];
root[0] = root[1];
rout[1] = replacer ;
Вибір імен
Ім'я повинно виражати суть об'єкту
Чим ширша область видимості об'єкту, тим важливіша виразність його імені. Імена виду x, i, k, тощо, якщо вживаються, то вказують на те, що їх область видимості кілька рядків
Приклади
currentDate, linesPerPage
done, found
Дисципліна іменування
Корисно називати об'єкти так, щоб ім'я виражало його роль. Корисно розрізняти
імена типів, наприклад, size_t
імена структур і класів ― зазвичай з великої літери, наприклад, Point, Collection
імена членів структур, наприклад, _x
імена констант, наприклад, LOCAL_CONSTANT, kConst
глобальні імена, наприклад, gThisGlobal
Особливо важлива дисципліна іменування у великих проектах
0 comments
Post a comment