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 الدوال المساعدة (coroutines) والمفاهيم (concepts) والنطاقات (ranges).

على عكس Java وKotlin، لا تحتوي C++ على جامع قمامة — يقوم المطور بإدارة تخصيص الذاكرة وتحريرها يدوياً عبر عاملي new وdelete. وهذا يمنح تحكماً كاملاً بالأداء لكنه يتطلب انضباطاً. وفقاً لاستطلاع JetBrains Developer Survey (2025)، يستخدم 42% من مطوري C++ اللغة في صناعة الألعاب.

معايير C++ تتطور كل ثلاث سنوات: C++11 (lambdas، auto، unique_ptr)، C++14 (lambdas العامة)، C++17 (if constexpr، filesystem)، C++20 (الوحدات، coroutines، concepts). يدعم المترجمان 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، ألعاب، مكتباتتطبيقات واجهة المستخدم، منطق الأعمال
البناء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 (واجهة ثنائية التطبيق): arm64-v8a (أجهزة ARM 64 بت، 99% من الهواتف الذكية الحديثة)، armeabi-v7a (ARM 32 بت، أجهزة قديمة)، 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++ من ملفات الرأس للتسلسل والـ blueprints والتكرار الشبكي. يُعرض MoveSpeed في المحرر عبر UPROPERTY(EditAnywhere).

Unity: C++ في الإضافات وSDK الأصلي

Unity تستخدم C# لمنطق اللعبة، لكن C++ تُستخدم في الإضافات الأصلية — مكتبات .so لنظام Android و.bundle لنظام iOS. عبر Android NDK يمكن ربط SDK C++ لشبكات الإعلانات والتحليلات أو نقل مكتبة 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 دون تحرير الذاكرة. تتم معالجة حلقة for على وحدة المعالجة المركزية بأقصى أداء لـ 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 أعداد عائمة من الذاكرة إلى سجل NEON 128 بت. يقوم vmulq_f32 بضرب جميع العناصر الأربعة في 0.5 في دورة واحدة. يقوم vst1q_f32 بحفظ النتيجة عائدة. بدون SIMD، كان نفس الحلقة سينفذ 4 عمليات ضرب تسلسلياً — يوفر NEON تسريعاً يصل إلى 4x لمثل هذه العمليات.

العمليةC++ (مللي ثانية)Java/Kotlin (مللي ثانية)التسريع
معالجة صورة 1920x108012453.75x
تحويل فورييه السريع (FFT)8344.25x
تشفير AES-256 لكتلة 1 ميجابايت3186x
ضغط بيانات ZLIB5224.4x

الأخطاء الشائعة عند استخدام C++ في المشاريع المحمولة

الخطأ الأول والأخطر هو تسرب الذاكرة. كل new يجب أن يقابله delete، وكل new[] يجب أن يقابله delete[]. على الأجهزة المحمولة ذات الذاكرة المحدودة، أي تسرب يؤدي إلى OOM (نفاد الذاكرة) وتعطل التطبيق. استخدم 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، سيتجاوز الحمل الزائد العمل المفيد. مرر المصفوفات كاملة دفعة واحدة وعالجها في جانب 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 ومكتبات النظام الأخرى.

هل JNI في C++ إلزامي لنظام Android؟

JNI (Java Native Interface) هو الطبقة الفاصلة بين كود Java/Kotlin لتطبيق Android ومكتبة C++. إذا تم استدعاء C++ من Java، فإن JNI إلزامي. يمكن كتابة كود C++ بالكامل عبر NDK، لكن نقطة الدخول عادة ما تكون Activity في Java تستدعي الدوال الأصلية.

متى يكون C++ مبرراً بدلاً من Kotlin في تطوير التطبيقات المحمولة؟

C++ مبرر للألعاب ومعالجة الصوت/الفيديو في الوقت الفعلي ورؤية الكمبيوتر والتشفير والمحاكاة الفيزيائية ونقل المكتبات من المنصات الأخرى. لتطبيق واجهة مستخدم عادي، 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. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا