Програмен език C++
Част 27
.
Запазване на променливите
(Storage Classes)
.
(съдържание)
.
В тази лекция ще бъдат разгледани различните дефиниции и декларации на променливите в C++, които определят видимостта (обхвата) на променливите (на английски visibility или scope) и времето на живот на променливите (lifetime). На английски тази концепция се нарича (storage classes), но споменаването на клас няма нищо общо с класовете в ООП, а просто означава "вид" или "тип" на запазване на променливите.

Концепцията за запазване на променливите произтича от това, че програмата се състои от функции и класове и това спомага за улесняване писането на програмен код, а от друга страна тази концепция прави по-ефективно използването на паметта в програмите и ускорява тяхното изпълнение.

1. Още нещо за декларацията и дефиницията. В някои от предишните материали беше пояснена разликата между декларация (declaration) и дефиниция (definition), но нека ги разгледаме по-подробно. Декларацията определя името и типа на данните, докато дефиницията прави не само това, но и казва на компилатора да запази памет за променливата - вижте дефинициите в линковете по-горе. Тази разлика досега беше правена само за функциите и последният пример бе от предишния материал в програмата overfun_def.cpp. В класа fancy_text функцията box_display беше първо декларирана

   void box_display(char ch = '-', int n = MAX_LENGTH);

и после дефинирана извън класа

void fancy_text::box_display(char ch, int n)
{
   int j;

   for (j = 0; j < n; j++)
      cout << ch;

   cout << endl << text << endl;

   for (j = 0; j < n; j++)
      cout << ch;
}

Според много автори, в C++ има два типа декларации на променливи - едните не запазват памет, а другите запазват памет. В настоящата лекция ще последва пример за първите, а всички разгледани декларации на променливи (от вида int ivar;) са от втория тип - т.е. запазват памет. Докато дефиницията винаги предизвиква запазване на памет за променливата.

Както споменахме, на практика концепцията за запазване на променливите определя две главни характеристики на променливите - видимостта (обхвата) на променливите (на английски visibility или scope) и времето на живот на променливите (lifetime). Обхватът на една променлива определя в кои части на програмата имаме достъп до променливата - тези части са клас, функция, файл или цялата програма. Времето на живот на променливата може да съвпада с това на обект, функция или цялата програма. Както ще видим в следващата точка всяко едно запазване на променливите определя дадена комбинация от обхват и време на живот.

2. Типове запазване на променливите (Storage Class). В предишните материали досега сме се запознали с един от Storage Class - автоматичните променливи (т. 3 от лекция 14). Тук ще разгледаме и другите комбинации на обхват и време на живот.

2.1. Автоматични променливи (Automatic Variables). Те са декларирани вътре във функциите и се създават автоматично когато функцията се извиква и се разрушават, когато тя връща управлението обратно след точката на извикване. От т. 3 от лекция 14 разбрахме, че тези променливи се създават в стека и после се премахват от него. Автоматичните променливи са видими само във функцията, в която са декларирани. Например една променлива с име var1, създадена в една функция func1() не може да бъде достъпна (т.е. да се извърши промяна или взимане на нейната стойност) в друга функция func2(). Това дава допълнително възможност за използването на автоматични променливи с едни и същи имена в различните функции, без да се безпокоим какво става с техните стойности - просто те са различни променливи.

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

Автоматичните променливи не се инициализират предварително и затова програмистът трябва да се погрижи те да получат първоначална стойност и да не разчита на това че те ще са 0 (съответно 0.0).

И последно за тези променливи - те могат да бъдат автоматични и при дефинирането им в блок, което е всяка една част от кода, заградена между двете големи скоби { и }. Също така променливи, дефинирани не само в тялото на функции, но и в тялото на цикли или if-else оператори са автоматични. При някои компилатори цикличните променливи дефинирани в условието на цикъла не могат да бъдат достъпни извън цикъла - например променливата i в цикъла по-долу.

for (int i = 0; i < 10; ++i)
  cout << i << endl;

2.2. Регистърни променливи (Register Variables). Това е специален вид автоматична променлива, дефинирана с ключовата дума register. Компилаторът ще се постарае да сложи променливата не в оперативната памет, а в регистъра на процесора. (Регистрите на процесора са също памети, в които се зареждат променливите и операциите, с които работи процесора и достъпът до регистрите е много по-бърз.)  Пример за променлива, подходяща за регистъра, е променливата на най-вътрешия цикъл при няколко цикъла, вкарани един в друг. На практика това е само препоръка към компилатора, с която той може да не се съобрази, а от друга страна съвременните компилатори са толкова гъвкаво програмирани, че сами могат да определят коя променлива е подходяща за регистърна променлива.

2.3. Външни променливи (External Variables). Освен че променливите могат да бъдат дефинирани във функциите (отделни или тези на класовете), то те могат да бъдат дефинирани директно в програмния код, извън всякакви функции или класове, както това е направено с MAX_LENGTH в програмата overfun_def.cpp от предишния материал .

const int MAX_LENGTH = 40;

Подобни деклариции имаше и в програмите cardaray.cpp и cardenum.cpp от лекция 22

const int jack = 11;         //2 through 10 are unnamed integers
const int queen = 12;
const int king = 13;
const int ace = 14;

и в програмата weekdays.cpp от лекция 20

const int MAX = 10;     // maximum length of day name, +1
const int DPW = 7;      // days per week

Външните променливи са видими в целия файл от точката на деклариране до края на файла. Тяхното време на живот е продължителността на програмата и те се инициализират по подразбиране с 0 (съответно за реалните 0.0).

2.4. Външни променливи и програма от няколко файла (External Variables and Multiple Files). Почти всички професионални програми (за разлика от нашите елементарни примери) се състоят от код, написан в няколко (десетки, даже стотици) файла. Една външна променлива е видима само във файла (от мястото на декларация до края му), в който е декларирана, но тя може да бъде направена видима (чрез декларация, която не заделя памет!) в друг файл чрез използване на ключовата дума extern. Ето един схематичен пример от книгата на Лафоор [1].

// file #1
int ivar;          // definition
// ivar is known in this file
// end of file #1

// file #2
extern int ivar;   // declaration
// ivar is also known in this file
// end of file #2

// file #3
// ivar is not known in this file
// end of file #3

В първия файл е дефинирана външна променлива ivar, която е декларирана във втория файл с ключовата дума extern и така тя е видима в този втори файл от това място до края му. Но в третия файл променливата е недостъпна.
.
!За всяка една променлива в дадена програма имаме само една дефиниция на тази променлива, защото може да се посочи само едно място в паметта, където се локализира променливата, но може да имаме няколко нейни декларации в други файлове с помощта на ключовата дума extern.

Освен това може да се ограничи видимостта на една външна променлива с помощта на ключовата дума static. Това не е статична променлива в смисъла на такава в класовете (ще се запознаем по-късно с тях), а просто е променлива с локална видимост (само във файла, където е дефинирана). Ето един схематичен пример от книгата на Лафоор [1].

// file #1
static int ivar;          // definition
// ivar can be known ONLY in this file
// end of file #1

// file #2
extern int ivar;          // useless declaration
// end of file #2

В първия файл е дефинирана статична външна променлива ivar, за която е направен опит да бъде декларирана във втория файл с ключовата дума extern и така тя да бъде видима в този втори файл от това място до края му. Но даже компилаторът да пропусне това логическо противоречие, то линкерът ще даде съобщение за грешка.

2.5. Статични локални променливи (Local Static Variables). Понякога програмистът иска функцията да запомня някаква стойност между нейните повиквания и това да не става с външни променливи. Тогава се използват т.н. статични локални променливи, които се дефинират вътре в тялото на функцията, но с ключовата дума static. Ето един пример от книгата на Лафоор [1] за функция, която изчислява средна стойност на няколко вкарани стойности.

float average(float item)
{
  static int n = 0;          // number of times we've been called
  static float total = 0.0;  // running total
  total += item;             // add argument to running total
  return total/++n;          // increment count, find average
}

Тези променливи съществуват по цялото време на изпълнение на програмата и се инициализират по подразбиране с 0 (съответно за реалните 0.0). Въпреки това, в кода по-горе изрично са инициализирани двете променливи - броя на вкараните стойности, n и тяхната сума, total, защото така става по-ясна логиката на програмата.

Статичните локални и външните променливи не се използват много в C++, защото затрудняват читаемостта на кода, пък и имат алтернатива в лицето на класовете и техните обекти, както ще видим по-късно.

3. Типове запазване на променливите за обектите. Обектите са точно като променливите и всичко, което написахме по-горе важи и за обектите, без регистърната автоматична променлива, която няма смисъл, тъй като един обект не може да бъде сложен в регистър на процесора.

Видимостта на променливите в класа зависи от ключовите думи private и public. Променливи на класа (instance data) дефинирани с ключовата дума private могат да бъдат достъпни само на функциите на класа (member functions) - даже не са достъпни чрез обектите на този клас. Променливи на класа (instance data) дефинирани с ключовата дума public  са  достъпни, както и на функциите на класа (member functions), така и на всички други функции, декларирани след мястото на класа: но тези променливи на класа са достъпни само чрез обектите на този клас (т.е чрез изполване на т.н. точков оператор (dot operator)  - obj1.var1).

Времето на живот на съответните променливи е това на самия обект - след дефиниране на обекта (неговото създаване в паметта) за тези променливи се запазва памет и те освобождават тази памет при разрушаване на обекта. Но мислете за тях като за променливи на даден обект, а не на всички обекти от този клас - това последното не важи за статичните променливи на обектите, нещо което ще разгледаме в следващата лекция.

Ето една таблица, която систематизира казаното досега за запазване на променливите и видимостта на променливите и тяхното време на живот.

Таблица 1. Типовете на  (storage classes).


запазване на променливите    Ключова дума          Видимост (Обхват)     Време на живот


автоматична                  или auto              във функцията         това на функцията
ригистърна автоматична       register              във функцията         това на функцията
локална статична             static                във функцията         това на програмата
външна при дефиниране        без                   във файла*            това на програмата
външна при декларация        extern                във файла             това на програмата
външна статична              extern                във файла**           това на програмата


* може да бъде декларирана в други файлове
** не може да бъде декларирана в други файлове

Винаги даваме и съответните термини на английски, затова ето таблица 1 в оригиналните термини.

Table 1. Storage class, visibility, and lifetime.


Storage Class               Specifier (Keyword)    Visibility (Scope)     Lifetime


Automatic                   None (or auto)         Function               Function
Register                    register               Function               Function
Local static                static                 Function               Program
External (definition)       None                   File*                  Program
External (declaration)      extern                 File                   Program
External static             static                 File**                 Program


* can be declared in other files
** cannot be declared in other files
.
(съдържание)
.
Литература
.
[1] Robert Lafore; C++ Interactive Course. Waite Group Press, Macmillan Computer Publishing, 1996.

Автор: Проф. Процесор Аритметиков

.
[ това е материал от брой 29 от април 2009 г на списание "Коснос" www.kosnos.com ]

Keywords: С++,  OOP programming , C++ , Classes , Inheritance , Reusability , Creating New Data Types , Polymorphism and Overloading
Ключови думи: клас , обект, обектно ориентирано програмиране , полиморфизъм switch if else ?