C++ w programowaniu mobilnym: Android NDK, Unity i Unreal Engine

Autor: IT Sectr Opublikowano: 2026-02-10 Czas czytania: 11 min

C++ — wieloparadygmatowy język programowania z ręcznym zarządzaniem pamięcią, łączący programowanie proceduralne, obiektowe i uogólnione. W programowaniu mobilnym C++ jest używany przez Android NDK do przenoszenia bibliotek oraz przez silniki gier Unity i Unreal Engine do tworzenia gier wieloplatformowych. Według TIOBE (2026), C++ znajduje się w pierwszej piątce najpopularniejszych języków wśród profesjonalnych programistów.

Najważniejsze

  • C++ — język programowania systemowego używany w Android NDK, Unity i Unreal Engine
  • Android NDK umożliwia kompilację kodu C++ do natywnych bibliotek .so dla procesorów ARM
  • Unreal Engine jest w całości zbudowany na C++, a Unity używa C++ dla niskopoziomowych wtyczek
  • JNI (Java Native Interface) — most między Java/Kotlin a C++ na Androidzie
  • CMake i NDK Build — systemy budowania dla projektów C++ na platformy mobilne

Czym jest C++?

C++ — język programowania stworzony przez Bjarne Stroustrupa w 1985 roku jako rozszerzenie języka C o obsługę klas. C++ zapewnia bezpośredni dostęp do pamięci przez wskaźniki, metaprogramowanie szablonowe i dziedziczenie wielokrotne. Standardy C++17 i C++20 dodały korutyny, koncepty i ranges.

W przeciwieństwie do Javy i Kotlina, C++ nie ma garbage collectora — programista ręcznie zarządza alokacją i zwalnianiem pamięci przez operatory new i delete. Daje to pełną kontrolę nad wydajnością, ale wymaga dyscypliny. Według JetBrains Developer Survey (2025), 42% programistów C++ używa tego języka w branży gier.

Standardy C++ ewoluują co trzy lata: C++11 (lambdy, auto, unique_ptr), C++14 (lambdy uogólnione), C++17 (if constexpr, filesystem), C++20 (moduły, korutyny, koncepty). Kompilatory Clang i GCC obsługują wszystkie aktualne standardy, a dla Android NDK używany jest Clang z toolchaina LLVM.

Różnice między C++ a Java i Kotlin

Główna różnica C++ od języków JVM — ręczne zarządzanie pamięcią i kompilacja bezpośrednio do kodu maszynowego. Java i Kotlin działają na maszynie wirtualnej z automatycznym garbage collectorem. Kod C++ jest kompilowany do natywnego pliku binarnego dla konkretnej architektury — ARM, x86 lub RISC-V.

KryteriumC++Java / Kotlin
Zarządzanie pamięciąRęczne (new/delete, RAII)Automatyczne (Garbage Collector)
KompilacjaNatywny kod maszynowyKod bajtowy JVM / ART
WydajnośćMaksymalna (zero-cost abstractions)Wysoka z pauzami GC
Zastosowanie w programowaniu mobilnymNDK, gry, bibliotekiAplikacje UI, logika biznesowa
BudowanieCMake, NDK Build, Gradle + NDKGradle, Maven
WieloplatformowośćKompilacja dla każdej platformyJVM / ART wszędzie

C++ w programowaniu mobilnym: Android NDK

Android NDK (Native Development Kit) — zestaw narzędzi Google do pisania części aplikacji Android w C i C++. NDK zawiera toolchain oparty na Clang, systemowe pliki nagłówkowe Androida, biblioteki do debugowania i profilowania. Wersja r27 (2025) obsługuje C++20 i używa LLVM 18.

NDK kompiluje kod C++ do bibliotek dynamicznych .so dla architektur ARM64, ARM32, x86 i x86_64. Aplikacja w Javie lub Kotlinie ładuje te biblioteki przez System.loadLibrary() i wywołuje funkcje native przez JNI. Według Google, NDK został pobrany ponad 5 milionów razy.

Struktura projektu z NDK

Typowy projekt z Android NDK zawiera folder app/src/main/cpp/ z plikami źródłowymi C++, CMakeLists.txt do budowania i klasę Java z deklaracją metod native. Gradle podłącza NDK przez blok externalNativeBuild w build.gradle.

cpp
// native-lib.cpp — prosty JNI-metoda dla Androida
#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());
}

Kod deklaruje funkcję JNI z pełną nazwą Java_com_example_app_MainActivity_stringFromJNI — to konwencja nazewnictwa JNI. Funkcja przyjmuje wskaźnik na JNIEnv (środowisko wykonawcze) i jobject (instancję klasy wywołującej). env->NewStringUTF tworzy ciąg Java z ciągu C++.

ABI i docelowe architektury

Android obsługuje kilka ABI (Application Binary Interface): arm64-v8a (64-bitowe urządzenia ARM, 99% nowoczesnych smartfonów), armeabi-v7a (32-bitowe ARM, starsze urządzenia), x86_64 (emulatory). NDK umożliwia budowanie .so dla każdej architektury przez filtry ABI w build.gradle.

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

C++ i silniki gier: Unity i Unreal Engine

C++ — główny język silników gier. Unreal Engine używa C++ dla całego kodu gry, a Unity wykorzystuje C++ w rdzeniu silnika do renderowania i fizyki. Nawet jeśli programista pisze w C# w Unity, pod maską C++ zarządza grafiką, dźwiękiem i obliczeniami fizycznymi.

Unreal Engine: C++ jako główny język

Unreal Engine jest zbudowany na C++ od podstaw. Kod logiki gry pisze się w C++ z użyciem makr UHT (Unreal Header Tool) do integracji z Blueprints — wizualnym systemem skryptowania. Klasy dziedziczą po typach bazowych AActor, UObject, UGameInstance z literami prefiksowymi U, A, F.

cpp
// Klasa postaci w Unreal Engine w 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;
};

Makra UCLASS() i UPROPERTY() — część systemu refleksji Unreal Engine. UHT (Unreal Header Tool) generuje kod C++ z plików nagłówkowych do serializacji, blueprintów i replikacji sieciowej. MoveSpeed jest wyświetlane w edytorze przez UPROPERTY(EditAnywhere).

Unity: C++ we wtyczkach i Native SDK

Unity używa C# dla logiki gry, ale C++ jest stosowany w Native Plugins — bibliotekach .so dla Androida i .bundle dla iOS. Przez Android NDK można podłączać C++ SDK sieci reklamowych, analityki lub przenosić bibliotekę C++ z gier desktopowych.

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);
    }
}

W tym przykładzie C# marshaluje wywołanie funkcji C++ ProcessFrame przez P/Invoke. Atrybut DllImport wskazuje nazwę biblioteki .so. Funkcja C++ przetwarza klatkę w czasie rzeczywistym i zwraca wynik. Takie podejście stosuje się dla post-efektów i wizji komputerowej.

JNI: współdziałanie Java i C++

JNI (Java Native Interface) — standardowy mechanizm wywoływania kodu C/C++ z Javy lub Kotlina. W Androidzie JNI to jedyny sposób przesyłania danych między maszyną wirtualną ART a biblioteką natywną. Każde wywołanie JNI ma narzut, dlatego dane przesyła się pakietami, a nie pojedynczymi polami.

JNIEnv — wskaźnik na tablicę funkcji JNI. env->FindClass ładuje klasę Java, env->GetMethodID pobiera identyfikator metody, env->CallVoidMethod ją wywołuje. Dla ciągów używa się GetStringUTFChars i ReleaseStringUTFChars. JNI obsługuje również referencje globalne i słabe do obiektów Java.

cpp
// Przekazanie tablicy bajtów z C++ do Javy przez 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; // odwracamy bajty
    }

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

GetByteArrayElements pobiera wskaźnik na tablicę bajtów Java. JNI_COMMIT w ReleaseByteArrayElements kopiuje zmiany z powrotem do tablicy Java bez zwalniania pamięci. Pętla for wykonuje przetwarzanie na CPU z maksymalną wydajnością C++ bez pauz GC.

Zarządzanie pamięcią w JNI

Główne niebezpieczeństwo JNI — wycieki referencji globalnych. Każdy NewGlobalRef musi być zwolniony przez DeleteGlobalRef. Referencje lokalne (zwracane przez FindClass, NewStringUTF) są automatycznie usuwane przy powrocie z funkcji native. Do intensywnych wywołań używaj PushLocalFrame / PopLocalFrame.

CMake i systemy budowania dla C++ na Androida

CMake — główne narzędzie budowania dla C++ w Android NDK. CMakeLists.txt opisuje cele budowania, biblioteki i zależności. Gradle wywołuje CMake przez externalNativeBuild, przekazując ścieżki do NDK i toolchaina. Alternatywa — NDK Build oparty na Android.mk i Application.mk.

CMake 3.22+ obsługuje presety (CMakePresets.json) dla różnych konfiguracji: debug, release, profile z różnymi flagami optymalizacji. Dla Androida używany jest toolchain android.toolchain.cmake z NDK, automatycznie podstawiany przez Gradle podczas budowania.

cmake
# CMakeLists.txt dla projektu 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) tworzy bibliotekę .so. target_link_libraries podłącza biblioteki systemowe android (dla JNI) i log (dla __android_log_print). Biblioteka statyczna libjpeg.a jest linkowana do pracy z obrazami JPEG. CXX_STANDARD 20 włącza obsługę C++20.

vcpkg i zarządzanie zależnościami

vcpkg — menedżer pakietów dla C++ od Microsoft, obsługujący Android NDK jako platformę docelową. Przez vcpkg można zainstalować Boost, OpenCV, SQLite, nlohmann-json i inne biblioteki skompilowane dla Android ABI. Vcpkg integruje się z CMake przez CMAKE_TOOLCHAIN_FILE.

Wydajność C++ na urządzeniach mobilnych

C++ zapewnia maksymalną wydajność na urządzeniach mobilnych dzięki kompilacji do natywnego kodu ARM i brakowi garbage collectora. Według testów Google (Android Performance Patterns, 2024), kod JNI w C++ wykonuje się 3–10 razy szybciej niż równoważny kod Java dla zadań intensywnych obliczeniowo.

Kluczowe czynniki wydajności C++ na platformach mobilnych: lokalność pamięci podręcznej (umieszczanie danych w ciągłej pamięci), instrukcje SIMD NEON dla ARM, asynchroniczna praca z JNI przez kolejki komunikatów. NEON — rozszerzenie SIMD ARM, umożliwiające przetwarzanie 128 bitów danych w jednej instrukcji, niezbędne do przetwarzania audio, wideo i obrazów.

cpp
// Przetwarzanie tablicy SIMD przez 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 ładuje 4 float z pamięci do 128-bitowego rejestru NEON. vmulq_f32 mnoży wszystkie 4 elementy przez 0.5 w jednym cyklu. vst1q_f32 zapisuje wynik z powrotem. Bez SIMD ta sama pętla wykonałaby 4 mnożenia sekwencyjnie — NEON daje przyspieszenie do 4x dla takich operacji.

OperacjaC++ (ms)Java/Kotlin (ms)Przyspieszenie
Przetwarzanie obrazu 1920x108012453.75x
Szybka transformacja Fouriera (FFT)8344.25x
Szyfrowanie AES-256 bloku 1MB3186x
Kompresja danych ZLIB5224.4x

Typowe błędy przy używaniu C++ w projektach mobilnych

Pierwszy i najniebezpieczniejszy błąd — wyciek pamięci. Każdy new musi mieć delete, każdy new[] — delete[]. Na urządzeniach mobilnych z ograniczonym RAM każdy wyciek prowadzi do OOM (Out Of Memory) i awarii aplikacji. Używaj RAII przez inteligentne wskaźniki: std::unique_ptr, std::shared_ptr, std::weak_ptr.

Drugi błąd — ignorowanie zgodności ABI. Biblioteka skompilowana dla armeabi-v7a nie załaduje się na urządzeniu ARM64. Zawsze określaj abiFilters w build.gradle i testuj na rzeczywistych urządzeniach, a nie tylko emulatorze x86_64. Używaj Android App Bundle do automatycznej filtracji ABI.

Trzeci typowy błąd — częste wywołania JNI. Każde przejście między Java a C++ kosztuje około 50–100 ns. Jeśli przetwarzasz tablicę 10000 elementów po jednym przez JNI, narzut przekroczy użyteczną pracę. Przekazuj dane całymi tablicami i przetwarzaj po stronie C++.

Problemy z wielowątkowością — kolejny częsty błąd. C++ może uruchamiać std::thread, ale dostęp do JNI jest możliwy tylko z wątku utworzonego przez Java. Do wywoływania metod Java z wątku C++ używaj AttachCurrentThread / DetachCurrentThread przez JNI.

cpp
// Prawidłowe wywołanie Java z wątku C++
JavaVM *g_vm; // globalny wskaźnik, uzyskany przy 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();
    }
}

Często zadawane pytania

Czym jest Android NDK i po co w nim C++?

Android NDK (Native Development Kit) — zestaw narzędzi do pisania części aplikacji w C lub C++. Jest używany do zadań krytycznych dla wydajności, przenoszenia bibliotek C/C++ na Androida i implementacji kodu wieloplatformowego Unity i Unreal Engine.

Które gry w Unity pisze się w C++?

W Unity kod logiki gry pisze się w C#, ale C++ jest używany w połączeniu z Android NDK do wtyczek, pracy z akceleracją sprzętową, przenoszenia własnych bibliotek C++ i integracji natywnych SDK dla reklam i analityki.

Jak skonfigurować CMakeLists.txt dla C++ na Androida?

CMakeLists.txt opisuje pliki źródłowe, biblioteki i flagi kompilacji. Minimalna konfiguracja obejmuje add_library do budowania .so i target_link_libraries do podłączenia Android log i innych bibliotek systemowych.

Czy JNI w C++ jest obowiązkowe dla Androida?

JNI (Java Native Interface) to warstwa pośrednia między kodem Java/Kotlin aplikacji Android a biblioteką C++. Jeśli C++ jest wywoływany z Javy, JNI jest obowiązkowe. Można pisać kod C++ w całości przez NDK, ale punktem wejścia jest zwykle aktywność Java wywołująca funkcje native.

Kiedy C++ jest uzasadniony zamiast Kotlina w programowaniu mobilnym?

C++ jest uzasadniony dla gier, przetwarzania audio/wideo w czasie rzeczywistym, wizji komputerowej, kryptografii, symulacji fizycznych i przenoszenia bibliotek z innych platform. Do zwykłej aplikacji UI Kotlin jest wydajniejszy i prostszy.

Podsumowanie

  • C++ — wieloparadygmatowy język z ręcznym zarządzaniem pamięcią, używany w Android NDK, Unity i Unreal Engine do kodu o wysokiej wydajności
  • Android NDK kompiluje C++ do bibliotek .so dla architektur ARM z toolchainem Clang z LLVM
  • Unreal Engine jest w całości zbudowany na C++ z systemem refleksji UHT, Unity używa C++ w rdzeniu renderowania i fizyki
  • JNI — obowiązkowy most między Java/Kotlin a C++ na Androidzie z konwencją nazewnictwa Java___
  • CMake — główny system budowania dla C++ w NDK, obsługujący C++20 i integrację z Gradle
  • NEON SIMD zapewnia do 4x przyspieszenia przetwarzania danych na procesorach ARM przez 128-bitowe instrukcje wektorowe
  • Typowe błędy: wycieki pamięci, nieprawidłowy ABI w abiFilters, częste wywołania JNI i wielowątkowość bez AttachCurrentThread

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również