Ключове слово static
є одним з найзаплутаніших у мові C++. Воно має різні значення в різних ситуаціях.
На уроці про глобальні змінні ми дізналися, що додаючи static
до змінної, оголошеної поза блоком, ми визначаємо її як внутрішню, тобто таку, яку можна використовувати тільки в файлі, в якому вона визначена.
Ключове слово static
можна застосовувати і до змінних всередині блоку, але тоді його значення буде іншим. На уроці про локальні змінні ми дізналися, що локальні змінні мають автоматичну тривалість життя, тобто створюються, коли блок починається, і знищуються при виході з нього.
Використання ключового слова static з локальними змінними змінює їх тривалість життя з автоматичної на статичну (або “фіксовану”). Статична змінна зберігає своє значення навіть після виходу з блоку, в якому вона визначена. Тобто вона створюється (і ініціалізується) тільки один раз, а потім зберігається протягом виконання всієї програми.
Розглянемо різницю між змінними з автоматичною і статичною тривалостями життя.
Автоматична тривалість життя (за замовчуванням):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> void incrementAndPrint() { int value = 1; // автоматична тривалість життя (за замовчуванням) ++value; std::cout << value << std::endl; } // змінна value знищується тут int main() { incrementAndPrint(); incrementAndPrint(); incrementAndPrint(); } |
Кожен раз, при виклику функції incrementAndPrint(), створюється змінна value
, якій присвоюється значення 1
. Функція incrementAndPrint() збільшує значення змінної до 2
, а потім виводить його. Коли функція incrementAndPrint() завершує своє виконання, змінна виходить з області видимості і знищується. Отже, результат виконання програми:
2
2
2
Тепер розглянемо статичну версію. Єдина різниця між цими двома програмами тільки в доданні ключового слова static
до змінної.
Статична тривалість життя:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> void incrementAndPrint() { static int s_value = 1; // змінна s_value є статичною ++s_value; std::cout << s_value << std::endl; } // змінна s_value не знищується тут, але стає недоступною int main() { incrementAndPrint(); incrementAndPrint(); incrementAndPrint(); } |
Оскільки змінна s_value
оголошена статичною (за допомогою ключового слова static
), то вона створюється і ініціалізується тільки один раз. Але, виходячи з області видимості, вона не знищується. Кожен раз, при виконанні функції incrementAndPrint(), значення s_value
збільшується.
Результат виконання програми:
2
3
4
Так само, як ми використовуємо префікс g_
з глобальними змінними, префікс s_
прийнято використовувати зі статичними змінними. Зверніть увагу, внутрішні глобальні змінні (також оголошені з використанням static
) залишаються з префіксом g_
, а не з префіксом s_
.
Навіщо потрібні статичні локальні змінні? Одним з найбільш поширених застосувань є генерація унікальних ідентифікаторів. При роботі з великою кількістю однакових об’єктів всередині програми часто буває корисно присвоювати кожному об’єкту окремий унікальний ідентифікаційний номер. Це легко здійснити, використовуючи одну статичну локальну змінну:
1 2 3 4 5 |
int generateID() { static int s_itemID = 0; return s_itemID++; } |
При першому виклику функції повертається 0
. Потім повертається 1
. Після цього 2
і кожний наступний виклик збільшуватиме цю змінну на одиницю. Хороший спосіб генерації унікальних ідентифікаторів для схожих об’єктів? Хороший! Оскільки s_itemID
— це локальна змінна, то вона не може бути «змінена» іншими функціями.
Статичні змінні мають деякі переваги глобальних змінних (вони не знищуються до завершення програми), зберігаючи при цьому локальну область видимості. Таким чином, вони набагато безпечніші для використання, ніж глобальні змінні.
Тест
Який ефект від додання ключового слова static
до глобальної змінної? Який вплив воно має на локальну змінну?
Відповідь
Додаючи ключове слово static
до глобальної змінної, ми визначаємо її як внутрішню, тобто таку, яку не можна експортувати і використовувати в інших файлах.
У випадку з локальною змінною, додання static
визначає її як статичну, тобто таку, яка створюється і ініціалізується тільки один раз і не знищується до самого кінця програми.