У деяких випадках в мові C++ змінна може бути потрібна тільки тимчасово. Наприклад:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> int add(int a, int b) { int result = a + b; return result; } int main() { std::cout << add(4, 2); return 0; } |
У функції add() змінна result
використовується як тимчасова змінна. Вона не має якогось особливого призначення, функція використовує її тільки для повернення значення. Є простіший спосіб написати функцію add() — через анонімний об’єкт.
Анонімний об’єкт — це значення без імені. Оскільки імені немає, то і способу посилатися на цей об’єкт за межами місця, де він створений — теж немає. Отже, анонімні об’єкти мають область видимості виразу і вони створюються, обробляються і знищуються в межах одного виразу.
Ось вищенаведена функція add(), але вже з використанням анонімного об’єкту:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> int add(int a, int b) { return a + b; // анонімний об'єкт створюється для зберігання і повернення результату виразу a + b } int main() { std::cout << add(4, 2); return 0; } |
При обчисленні виразу a + b
, результат поміщається в анонімний об’єкт. Потім копія анонімного об’єкту повертається по значенню назад в caller, і анонімний об’єкт знищується.
Це працює не тільки зі значеннями, що повертаються, але і з параметрами функції. Наприклад, замість наступного:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> void printResult(int value) { std::cout << value; } int main() { int result = 4 + 2; printResult(result); return 0; } |
Ми можемо написати наступне:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <iostream> void printResult(int value) { std::cout << value; } int main() { printResult(4 + 2); return 0; } |
У цьому випадку вираз 4 + 2
генерує результат 6
, який поміщається в анонімний об’єкт. Потім копія цього анонімного об’єкта передається в функцію printResult() (яка виводить значення 6
) і знищується.
Зверніть увагу, наскільки чистішим став наш код — нам не потрібно захаращувати його тимчасовими змінними, які використовуються тільки один раз.
Анонімні об’єкти класу
Хоча у вищенаведених прикладах ми використовували тільки фундаментальні типи даних, анонімні об’єкти також можуть використовуватися і з класами. Достатньо просто не вказувати ім’я об’єкту:
1 2 |
Dollars dollars(7); // звичайний об'єкт класу Dollars(8); // анонімний об'єкт класу |
У вищенаведеному прикладі рядок Dollars(8);
створить анонімний об’єкт класу Dollars, ініціалізує його значенням 8
, а потім знищить. У цьому контексті користі ми багато не отримаємо. Розглянемо приклад, де це може бути корисним:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <iostream> class Dollars { private: int m_dollars; public: Dollars(int dollars) { m_dollars = dollars; } int getDollars() const { return m_dollars; } }; void print(const Dollars &dollars) { std::cout << dollars.getDollars() << " dollars."; } int main() { Dollars dollars(7); print(dollars); return 0; } |
Тут функція main() передає об’єкт dollars
в функцію print(). Ми можемо спростити цю програму, використовуючи анонімні об’єкти:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <iostream> class Dollars { private: int m_dollars; public: Dollars(int dollars) { m_dollars = dollars; } int getDollars() const { return m_dollars; } }; void print(const Dollars &dollars) { std::cout << dollars.getDollars() << " dollars."; } int main() { print(Dollars(7)); // тут ми передаємо анонімний об'єкт класу Dollars return 0; } |
Результат виконання програми:
7 dollars.
Тепер розглянемо складніший приклад:
1 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 |
#include <iostream> class Dollars { private: int m_dollars; public: Dollars(int dollars) { m_dollars = dollars; } int getDollars() const { return m_dollars; } }; Dollars add(const Dollars &d1, const Dollars &d2) { Dollars sum = Dollars(d1.getDollars() + d2.getDollars()); return sum; } int main() { Dollars dollars1(7); Dollars dollars2(9); Dollars sum = add(dollars1, dollars2); std::cout << "I have " << sum.getDollars() << " dollars." << std::endl; return 0; } |
У функції add() у нас є значення змінної sum
класу Dollars, яке використовується в якості проміжного значення для зберігання результату і його повернення. І в функції main() у нас є значення змінної sum
класу Dollars, яке також використовується, як проміжне.
Можна зробити простіше, використовуючи анонімні об’єкти:
1 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 |
#include <iostream> class Dollars { private: int m_dollars; public: Dollars(int dollars) { m_dollars = dollars; } int getDollars() const { return m_dollars; } }; Dollars add(const Dollars &d1, const Dollars &d2) { return Dollars(d1.getDollars() + d2.getDollars()); // повертаємо анонімний об'єкт класу Dollars } int main() { Dollars dollars1(7); Dollars dollars2(9); std::cout << "I have " << add(dollars1, dollars2).getDollars() << " dollars." << std::endl; // виводимо анонімний об'єкт класу Dollars return 0; } |
Ця версія функції add() ідентична вищенаведеній функції add(), за винятком того, що замість окремого об’єкта використовується анонімний об’єкт класу Dollars. Також зверніть увагу, що в функції main() ми більше не використовуємо змінну з ім’ям sum
. Замість неї ми використовуємо анонімне значення, що повертається, функції add()!
В результаті, наша програма стала коротшою, чистішою і простішою. Фактично, оскільки dollars1
і dollars2
використовуються тільки в одному місці, ми можемо ще спростити цей код за допомогою анонімних об’єктів:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <iostream> class Dollars { private: int m_dollars; public: Dollars(int dollars) { m_dollars = dollars; } int getDollars() const { return m_dollars; } }; Dollars add(const Dollars &d1, const Dollars &d2) { return Dollars(d1.getDollars() + d2.getDollars()); // повертаємо анонімний об'єкт класу Dollars } int main() { std::cout << "I have " << add(Dollars(7), Dollars(9)).getDollars() << " dollars." << std::endl; // виводимо анонімний об'єкт класу Dollars return 0; } |
Висновки
Анонімні об’єкти в мові C++ використовуються для передачі або повернення значень без необхідності створювати величезну кількість тимчасових змінних. Динамічне виділення пам’яті також виконується через анонімні об’єкти (тому адреса виділеної пам’яті повинна бути присвоєна вказівнику, інакше ми не мали б можливості посилатися/використовувати її).
Варто ще відзначити, що анонімні об’єкти розглядаються як r-values (а не як l-values, у яких є адреси). Це означає, що анонімні об’єкти можуть передаватися або повертатися тільки по значенню або по константному посиланню. В інших випадках повинна використовуватися змінна.
Пам’ятайте, що анонімні об’єкти можна використовувати тільки один раз, так як вони мають область видимості виразу. Якщо вам потрібно посилатися на значення в декількох виразах, то для цього слід використовувати окрему змінну.