C++ هي لغة برمجة متعددة النماذج مع إدارة يدوية للذاكرة، تجمع بين البرمجة الإجرائية والموجهة للكائنات والبرمجة العامة. في تطوير التطبيقات المحمولة، يُستخدم C++ عبر Android NDK لنقل المكتبات وعبر محركي الألعاب Unity وUnreal Engine لإنشاء ألعاب متعددة المنصات. وفقاً TIOBE (2026)، يدخل C++ في قائمة أفضل 5 لغات من حيث الشعبية بين المطورين المحترفين.
الخلاصة
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++ ولغات 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 + NDK | Gradle، Maven |
| التعدد المنصات | ترجمة لكل منصة | JVM / ART في كل مكان |
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 ملايين مرة.
يحتوي مشروع نموذجي مع Android NDK على مجلد app/src/main/cpp/ مع ملفات مصدر C++، وCMakeLists.txt للبناء، وفئة Java مع إعلان الدوال الأصلية. يربط Gradle NDK عبر كتلة externalNativeBuild في build.gradle.
// 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++.
يدعم Android عدة ABI (واجهة ثنائية التطبيق): arm64-v8a (أجهزة ARM 64 بت، 99% من الهواتف الذكية الحديثة)، armeabi-v7a (ARM 32 بت، أجهزة قديمة)، x86_64 (المحاكيات). يسمح NDK ببناء .so لكل بنية عبر مرشحات ABI في build.gradle.
android {
defaultConfig {
ndk {
abiFilters "arm64-v8a", "armeabi-v7a"
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.22.1"
}
}
}C++ هي اللغة الأساسية لمحركات الألعاب. يستخدم Unreal Engine C++ لكل كود اللعبة، بينما يستخدم Unity C++ في نواة المحرك للعرض والفيزياء. حتى إذا كتب المطور بلغة C# في Unity، فإن C++ تدير تحت الغطاء الرسومات والصوت والحسابات الفيزيائية.
تم بناء Unreal Engine على C++ من الصفر. يُكتب كود منطق اللعبة بلغة C++ باستخدام وحدات ماكرو UHT (Unreal Header Tool) للتكامل مع Blueprints — نظام البرمجة النصية البصري. ترث الفئات من الأنواع الأساسية AActor وUObject وUGameInstance بأحرف البادئة U وA وF.
// فئة الشخصية في 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# لمنطق اللعبة، لكن C++ تُستخدم في الإضافات الأصلية — مكتبات .so لنظام Android و.bundle لنظام iOS. عبر Android NDK يمكن ربط SDK C++ لشبكات الإعلانات والتحليلات أو نقل مكتبة C++ من ألعاب سطح المكتب.
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 Native Interface) هي الآلية القياسية لاستدعاء كود C/C++ من Java أو Kotlin. في Android، JNI هي الطريقة الوحيدة لنقل البيانات بين الآلة الافتراضية ART والمكتبة الأصلية. كل استدعاء JNI له تكلفة إضافية، لذا يجب نقل البيانات دفعات وليس حقلاً تلو الآخر.
JNIEnv هو مؤشر لجدول دوال JNI. يقوم env->FindClass بتحميل فئة Java، وenv->GetMethodID يحصل على معرف الدالة، وenv->CallVoidMethod يستدعيها. للسلاسل النصية يُستخدم GetStringUTFChars وReleaseStringUTFChars. يدعم JNI أيضاً المراجع العالمية والضعيفة لكائنات Java.
// نقل مصفوفة البايت من 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 هو تسرب المراجع العالمية. يجب تحرير كل NewGlobalRef عبر DeleteGlobalRef. يتم تحرير المراجع المحلية (التي تُرجعها FindClass، NewStringUTF) تلقائياً عند العودة من الدالة الأصلية. للاستدعاءات المكثفة، استخدم PushLocalFrame / PopLocalFrame.
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 تلقائياً أثناء البناء.
# 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 هو مدير حزم لـ C++ من Microsoft يدعم Android NDK كمنصة مستهدفة. عبر vcpkg يمكن تثبيت Boost وOpenCV وSQLite وnlohmann-json ومكتبات أخرى مُجمَّعة لـ Android ABI. يتكامل Vcpkg مع CMake عبر CMAKE_TOOLCHAIN_FILE.
C++ توفر أقصى أداء على الأجهزة المحمولة بفضل الترجمة إلى كود ARM أصلي وغياب جامع القمامة. وفقاً لاختبارات Google (Android Performance Patterns، 2024)، يتم تنفيذ كود JNI بلغة C++ أسرع بمقدار 3–10 مرات من كود Java المكافئ للمهام ذات الحسابات المكثفة.
العوامل الرئيسية لأداء C++ على المنصات المحمولة: موقعية الذاكرة المؤقتة (ترتيب الذاكرة المتجاورة)، تعليمات SIMD NEON لـ ARM، الاتصال غير المتزامن مع JNI عبر قوائم الرسائل. NEON هي إضافة SIMD من ARM، تعالج 128 بت من البيانات في تعليمة واحدة، لا غنى عنها لمعالجة الصوت والفيديو والصور.
// معالجة 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 (مللي ثانية) | التسريع |
|---|---|---|---|
| معالجة صورة 1920x1080 | 12 | 45 | 3.75x |
| تحويل فورييه السريع (FFT) | 8 | 34 | 4.25x |
| تشفير AES-256 لكتلة 1 ميجابايت | 3 | 18 | 6x |
| ضغط بيانات ZLIB | 5 | 22 | 4.4x |
الخطأ الأول والأخطر هو تسرب الذاكرة. كل 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.
// استدعاء 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 (Native Development Kit) هو مجموعة أدوات لكتابة أجزاء من التطبيقات بلغة C أو C++. يُستخدم للمهام الحرجة من حيث الأداء، ونقل مكتبات C/C++ إلى Android، وتنفيذ كود متعدد المنصات لـ Unity وUnreal Engine.
في Unity، يُكتب كود منطق اللعبة بلغة C#، لكن C++ تُستخدم مع Android NDK للإضافات، وتسريع العتاد، ونقل مكتبات C++ الخاصة، ودمج SDK الأصلية للإعلانات والتحليلات.
CMakeLists.txt يصف ملفات المصدر والمكتبات وأعلام الترجمة. يتضمن التكوين الأدنى add_library لبناء .so وtarget_link_libraries لربط سجل Android ومكتبات النظام الأخرى.
JNI (Java Native Interface) هو الطبقة الفاصلة بين كود Java/Kotlin لتطبيق Android ومكتبة C++. إذا تم استدعاء C++ من Java، فإن JNI إلزامي. يمكن كتابة كود C++ بالكامل عبر NDK، لكن نقطة الدخول عادة ما تكون Activity في Java تستدعي الدوال الأصلية.
C++ مبرر للألعاب ومعالجة الصوت/الفيديو في الوقت الفعلي ورؤية الكمبيوتر والتشفير والمحاكاة الفيزيائية ونقل المكتبات من المنصات الأخرى. لتطبيق واجهة مستخدم عادي، Kotlin أكثر كفاءة وبساطة.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.