C++ у мобільній розробці: Android NDK, Unity та Unreal Engine

Автор: IT Sectr Опубліковано: 2026-02-10 Час читання: 11 хв

C++ — мультипарадигмальна мова програмування з ручним керуванням пам'яттю, що поєднує процедурне, об'єктно-орієнтоване та узагальнене програмування. У мобільній розробці C++ використовується через Android NDK для портування бібліотек та через ігрові рушії Unity та Unreal Engine для створення кроссплатформених ігор. За даними TIOBE (2026), C++ входить до топ-5 мов за популярністю серед професійних розробників.

Головне

  • C++ — мова системного програмування, що використовується в Android NDK, Unity та Unreal Engine
  • Android NDK дозволяє компілювати код C++ у нативні .so бібліотеки для ARM-процесорів
  • Unreal Engine повністю побудований на C++, а Unity використовує C++ для низькорівневих плагінів
  • JNI (Java Native Interface) — міст між Java/Kotlin та C++ на Android
  • CMake та NDK Build — системи збірки для C++ проєктів під мобільні платформи

Що таке C++?

C++ — мова програмування, створена Б'єрном Страуструпом у 1985 році як розширення мови C з підтримкою класів. C++ забезпечує прямий доступ до пам'яті через вказівники, шаблонне метапрограмування та множинне успадкування. Стандарти C++17 та C++20 додали корутини, концепти та ranges.

На відміну від Java та Kotlin, C++ не має збирача сміття — розробник вручну керує виділенням та звільненням пам'яті через оператори new та delete. Це дає повний контроль над продуктивністю, але потребує дисципліни. За даними JetBrains Developer Survey (2025), 42% розробників C++ використовують мову в ігровій індустрії.

Стандарти C++ еволюціонують кожні три роки: C++11 (лямбди, auto, unique_ptr), C++14 (узагальнені лямбди), C++17 (if constexpr, filesystem), C++20 (модулі, корутини, концепти). Компілятори Clang та GCC підтримують усі актуальні стандарти, а Android NDK використовує Clang з тулчейну LLVM.

Відмінності C++ від Java та Kotlin

Головна відмінність C++ від мов JVM — ручне керування пам'яттю та компіляція в машинний код напряму. Java та Kotlin працюють поверх віртуальної машини з автоматичним збиранням сміття. Код C++ компілюється в нативний бінарний файл для конкретної архітектури — ARM, x86 або RISC-V.

КритерійC++Java / Kotlin
Керування пам'яттюРучне (new/delete, RAII)Автоматичне (збирач сміття)
КомпіляціяНативний машинний кодБайт-код JVM / ART
ПродуктивністьМаксимальна (zero-cost abstractions)Висока з паузами GC
Використання в мобільній розробціNDK, ігри, бібліотекиUI застосунки, бізнес-логіка
ЗбіркаCMake, NDK Build, Gradle + NDKGradle, Maven
КроссплатформенністьКомпіляція під кожну платформуJVM / ART скрізь

C++ у мобільній розробці: Android NDK

Android NDK (Native Development Kit) — інструментарій Google для написання частин Android-застосунку на C та C++. NDK включає тулчейн на основі Clang, системні заголовні файли Android, бібліотеки для налагодження та профілювання. Версія r27 (2025) підтримує C++20 та використовує LLVM 18.

NDK компілює код C++ у динамічні бібліотеки .so для архітектур ARM64, ARM32, x86 та x86_64. Застосунок на Java або Kotlin завантажує ці бібліотеки через System.loadLibrary() та викликає нативні функції через JNI. За даними Google, NDK завантажено понад 5 мільйонів разів.

Структура проєкту з NDK

Типовий проєкт з Android NDK містить папку app/src/main/cpp/ з вихідними файлами C++, CMakeLists.txt для збірки та Java-клас з оголошенням нативних методів. Gradle підключає NDK через блок externalNativeBuild у build.gradle.

cpp
// native-lib.cpp — простий JNI-метод для Android
#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_app_MainActivity_stringFromJNI(
    JNIEnv *env,
    jobject /*thiz*/) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

Код оголошує JNI-функцію з повним іменем Java_com_example_app_MainActivity_stringFromJNI — це угода JNI про іменування. Функція приймає вказівник на JNIEnv (середовище виконання) та jobject (екземпляр викликаючого класу). env->NewStringUTF створює Java-рядок з рядка C++.

ABI та цільові архітектури

Android підтримує кілька ABI (Application Binary Interface): arm64-v8a (64-бітні ARM пристрої, 99% сучасних смартфонів), armeabi-v7a (32-бітні ARM, застарілі пристрої), x86_64 (емулятори). NDK дозволяє збирати .so для кожної архітектури через фільтри ABI у build.gradle.

gradle
android {
    defaultConfig {
        ndk {
            abiFilters "arm64-v8a", "armeabi-v7a"
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.22.1"
        }
    }
}

C++ та ігрові рушії: Unity та Unreal Engine

C++ — основна мова ігрових рушіїв. Unreal Engine використовує C++ для всього коду гри, а Unity застосовує C++ у ядрі рушія для рендерингу та фізики. Навіть якщо розробник пише на C# у Unity, під капотом C++ керує графікою, звуком та фізичними розрахунками.

Unreal Engine: C++ як основна мова

Unreal Engine побудований на C++ з нуля. Код ігрової логіки пишеться на C++ з використанням макросів UHT (Unreal Header Tool) для інтеграції з Blueprints — візуальною системою скриптингу. Класи успадковуються від базових типів AActor, UObject, UGameInstance з префіксними літерами U, A, F.

cpp
// Клас персонажа в Unreal Engine на C++
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"

UCLASS()
class AMyCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    AMyCharacter();

    virtual void BeginPlay() override;
    virtual void Tick(float DeltaTime) override;

    UPROPERTY(EditAnywhere, Category = "Movement")
    float MoveSpeed = 600.0f;

protected:
    void SetupPlayerInputComponent(UInputComponent *PlayerInputComponent) override;
};

Макроси UCLASS() та UPROPERTY() — частина системи рефлексії Unreal Engine. UHT (Unreal Header Tool) генерує код C++ із заголовних файлів для серіалізації, блупринтів та мережевої реплікації. MoveSpeed відображається в редакторі через UPROPERTY(EditAnywhere).

Unity: C++ у плагінах та Native SDK

Unity використовує C# для ігрової логіки, але C++ застосовується в Native Plugins — .so бібліотеках для Android та .bundle для iOS. Через Android NDK можна підключати C++ SDK рекламних мереж, аналітики або портувати бібліотеку C++ з десктопних ігор.

csharp
using System.Runtime.InteropServices;
using UnityEngine;

public class NativeBridge : MonoBehaviour
{
    const string LIB_NAME = "native_plugin";

    [DllImport(LIB_NAME, CallingConvention = CallingConvention.Cdecl)]
    private static extern int ProcessFrame(
        int width, int height,
        IntPtr pixelData
    );

    void Update()
    {
        int result = ProcessFrame(Screen.width, Screen.height, IntPtr.Zero);
        Debug.Log("Frame processed: " + result);
    }
}

У цьому прикладі C# маршалує виклик до функції C++ ProcessFrame через P/Invoke. Атрибут DllImport вказує ім'я .so бібліотеки. Функція C++ обробляє кадр у реальному часі та повертає результат. Такий підхід застосовується для пост-ефектів та комп'ютерного зору.

JNI: взаємодія Java та C++

JNI (Java Native Interface) — стандартний механізм для виклику C/C++ коду з Java або Kotlin. В Android JNI — єдиний спосіб передати дані між віртуальною машиною ART та нативною бібліотекою. Кожен JNI-виклик має накладні витрати, тому дані передають пакетами, а не по одному полю.

JNIEnv — вказівник на таблицю функцій JNI. env->FindClass завантажує Java-клас, env->GetMethodID отримує ідентифікатор методу, env->CallVoidMethod викликає його. Для рядків використовується GetStringUTFChars та ReleaseStringUTFChars. JNI також підтримує глобальні та слабкі посилання на Java-об'єкти.

cpp
// Передача масиву байтів з C++ до Java через JNI
extern "C" JNIEXPORT void JNICALL
Java_com_example_app_ImageProcessor_processArray(
    JNIEnv *env, jobject /*thiz*/,
    jbyteArray input, jint length) {

    jbyte *buffer = env->GetByteArrayElements(input, nullptr);
    jsize size = env->GetArrayLength(input);

    for (jsize i = 0; i < size; ++i) {
        buffer[i] = buffer[i] ^ 0xFF; // інвертуємо байти
    }

    env->ReleaseByteArrayElements(input, buffer, JNI_COMMIT);
}

GetByteArrayElements отримує вказівник на масив Java-байтів. JNI_COMMIT у ReleaseByteArrayElements копіює зміни назад у Java-масив без звільнення пам'яті. Циклічна обробка виконується на CPU з максимальною продуктивністю C++ без пауз GC.

Керування пам'яттю в JNI

Головна небезпека JNI — витоки глобальних посилань. Кожен NewGlobalRef має бути звільнений через DeleteGlobalRef. Локальні посилання (що повертаються FindClass, NewStringUTF) автоматично видаляються при поверненні з нативної функції. Для інтенсивних викликів використовуйте PushLocalFrame / PopLocalFrame.

CMake та системи збірки для C++ під Android

CMake — основний інструмент збірки для C++ в Android NDK. CMakeLists.txt описує цілі збірки, бібліотеки та залежності. Gradle викликає CMake через externalNativeBuild, передаючи шляхи до NDK та тулчейну. Альтернатива — NDK Build на основі Android.mk та Application.mk.

CMake 3.22+ підтримує пресети (CMakePresets.json) для різних конфігурацій: debug, release, profile з різними прапорами оптимізації. Для Android використовується тулчейн android.toolchain.cmake з NDK, що автоматично підставляється Gradle при збірці.

cmake
# CMakeLists.txt для Android NDK проєкту
cmake_minimum_required(VERSION 3.22)
project("native-lib")

add_library(
    native-lib
    SHARED
    src/main/cpp/native-lib.cpp
    src/main/cpp/image_utils.cpp
)

target_include_directories(
    native-lib PRIVATE
    src/main/cpp/include
)

target_link_libraries(
    native-lib
    android
    log
    ${CMAKE_SOURCE_DIR}/libs/libjpeg.a
)

set_target_properties(
    native-lib PROPERTIES
    CXX_STANDARD 20
    CXX_STANDARD_REQUIRED ON
)

add_library(SHARED) створює .so бібліотеку. target_link_libraries підключає системні бібліотеки android (для JNI) та log (для __android_log_print). Статична бібліотека libjpeg.a лінкується для роботи з JPEG-зображеннями. CXX_STANDARD 20 включає підтримку C++20.

vcpkg та керування залежностями

vcpkg — менеджер пакетів для C++ від Microsoft, що підтримує Android NDK як цільову платформу. Через vcpkg можна встановити Boost, OpenCV, SQLite, nlohmann-json та інші бібліотеки, скомпільовані для Android ABI. Vcpkg інтегрується з CMake через CMAKE_TOOLCHAIN_FILE.

Продуктивність C++ на мобільних пристроях

C++ забезпечує максимальну продуктивність на мобільних пристроях завдяки компіляції в нативний код ARM та відсутності збирача сміття. За тестами Google (Android Performance Patterns, 2024), JNI-код на C++ виконується в 3–10 разів швидше за еквівалентний код Java для завдань з інтенсивними обчисленнями.

Ключові фактори продуктивності C++ на мобільних платформах: кеш-локальність (розміщення даних у неперервній пам'яті), SIMD-інструкції NEON для ARM, асинхронна робота з JNI через черги повідомлень. NEON — SIMD-розширення ARM, що дозволяє обробляти 128 біт даних за одну інструкцію, незамінне для обробки аудіо, відео та зображень.

cpp
// SIMD-обробка масиву через NEON intrinsics
#include <arm_neon.h>

void process_audio_neon(float *data, size_t n) {
    for (size_t i = 0; i < n; i += 4) {
        float32x4_t vec = vld1q_f32(&data[i]);
        float32x4_t result = vmulq_f32(vec, vdupq_n_f32(0.5f));
        vst1q_f32(&data[i], result);
    }
}

vld1q_f32 завантажує 4 float з пам'яті в 128-бітний NEON регістр. vmulq_f32 множить усі 4 елементи на 0.5 за один такт. vst1q_f32 зберігає результат назад. Без SIMD той самий цикл виконав би 4 множення послідовно — NEON дає прискорення до 4x для таких операцій.

ОпераціяC++ (мс)Java/Kotlin (мс)Прискорення
Обробка зображення 1920x108012453.75x
Швидке перетворення Фур'є (FFT)8344.25x
Шифрування AES-256 блока 1MB3186x
Стиснення даних ZLIB5224.4x

Типові помилки при використанні C++ у мобільних проєктах

Перша і найнебезпечніша помилка — витік пам'яті. Кожен new повинен мати delete, кожен new[] — delete[]. На мобільних пристроях з обмеженою RAM будь-який витік призводить до OOM (Out Of Memory) та падіння застосунку. Використовуйте RAII через розумні вказівники: std::unique_ptr, std::shared_ptr, std::weak_ptr.

Друга помилка — ігнорування ABI-сумісності. Бібліотека, скомпільована для armeabi-v7a, не завантажиться на пристрої ARM64. Завжди вказуйте abiFilters у build.gradle та тестуйте на реальних пристроях, а не тільки емуляторі x86_64. Використовуйте Android App Bundle для автоматичної фільтрації ABI.

Третя типова помилка — часті JNI-виклики. Кожен перехід між Java та C++ коштує близько 50–100 нс. Якщо обробляти масив з 10000 елементів по одному через JNI, overhead перевищить корисну роботу. Передавайте дані масивами цілком та обробляйте на стороні C++.

Проблеми з багатопотоковістю — ще одна часта помилка. C++ може запускати std::thread, але звертатися до JNI можна тільки з потоку, створеного Java. Для виклику Java-методів з C++ потоку використовуйте AttachCurrentThread / DetachCurrentThread через JNI.

cpp
// Правильний виклик Java з C++ потоку
JavaVM *g_vm; // глобальний вказівник, отриманий при JNI_OnLoad

void background_task() {
    JNIEnv *env;
    jint res = g_vm->AttachCurrentThread(&env, nullptr);
    if (res == JNI_OK) {
        jclass clazz = env->FindClass("com/example/App");
        jmethodID mid = env->GetStaticMethodID(clazz, "onProgress", "(I)V");
        env->CallStaticVoidMethod(clazz, mid, 100);
        g_vm->DetachCurrentThread();
    }
}

Часті запитання

Що таке Android NDK і навіщо в ньому C++?

Android NDK (Native Development Kit) — інструментарій для написання частин застосунку на C або C++. Він використовується для критичних до продуктивності завдань, портування C/C++ бібліотек на Android та реалізації кроссплатформеного коду для Unity та Unreal Engine.

Які ігри на Unity пишуть на C++?

У Unity код ігрової логіки пишуть на C#, але C++ використовується в зв'язці з Android NDK для плагінів, роботи з апаратним прискоренням, портування власних бібліотек на C++ та інтеграції нативних SDK для реклами та аналітики.

Як налаштувати CMakeLists.txt для C++ під Android?

CMakeLists.txt описує вихідні файли, бібліотеки та прапори компіляції. Мінімальна конфігурація включає add_library для збірки .so та target_link_libraries для прив'язки Android log та інших системних бібліотек.

JNI в C++ — це обов'язково для Android?

JNI (Java Native Interface) — прошарок між Java/Kotlin кодом Android застосунку та бібліотекою C++. Якщо C++ викликається з Java, JNI обов'язковий. Можна писати код C++ повністю через NDK, але точка входу зазвичай Java-активність, що викликає нативні функції.

Коли C++ виправданий замість Kotlin у мобільній розробці?

C++ виправданий для ігор, обробки аудіо/відео в реальному часі, комп'ютерного зору, криптографії, фізичних симуляцій та портування бібліотек з інших платформ. Для звичайного UI-застосунку Kotlin ефективніший і простіший.

Підсумки

  • C++ — мультипарадигмальна мова з ручним керуванням пам'яттю, що використовується в Android NDK, Unity та Unreal Engine для високопродуктивного коду
  • Android NDK компілює C++ у .so бібліотеки для ARM-архітектур з тулчейном Clang з LLVM
  • Unreal Engine повністю побудований на C++ з системою рефлексії UHT, Unity використовує C++ у ядрі рендерингу та фізики
  • JNI — обов'язковий міст між Java/Kotlin та C++ на Android з конвенцією іменування Java_<пакет>_<клас>_<метод>
  • CMake — основна система збірки для C++ в NDK, що підтримує C++20 та інтеграцію з Gradle
  • NEON SIMD забезпечує до 4x прискорення обробки даних на ARM-процесорах через 128-бітні векторні інструкції
  • Типові помилки: витоки пам'яті, невірний ABI в abiFilters, часті JNI-виклики та багатопотоковість без AttachCurrentThread

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також