انتقل إلى المحتوى

الطبقات والهياكل والتغليف

فهم الفرق بين البنية والفئة، ومحددات الوصول، والتغليف، ووظائف الأعضاء الثابتة، والأعضاء الثابتة، والتهيئة الإجمالية الحديثة.

ما هي الدرجة؟

الفئة في C++ هي نوع محدد من قبل المستخدم يجمع البيانات (متغيرات الأعضاء) والعمليات على تلك البيانات (وظائف الأعضاء) في وحدة واحدة. يُطلق على هذا التجميع اسم التغليف — أحد الركائز الأربع لـ OOP.

الفكرة الأساسية هي أن الفصل يحدد الثابت: مجموعة من القواعد التي يجب أن تكون صحيحة دائمًا فيما يتعلق بحالتها الداخلية. على سبيل المثال، قد تتطلب فئةDateأن يكون الشهر دائمًا بين 1 و12. ويتيح لك التغليف فرض هذه الثوابت من خلال التحكم في الوصول إلى الحالة الداخلية.

الهيكل مقابل الطبقة

في C++،structوclassمتطابقان تقريبًا. والفرق الوحيد هو محدد الوصول الافتراضي:structالافتراضي هوpublic، بينماclassالافتراضي هوprivate. الاصطلاح: استخدمstructلتجميع البيانات البسيطة، وclassعندما يكون لديك ثوابت تحتاج إلى صيانتها.

struct_vs_class.cpp
#include <iostream>
#include <string>

// struct: members are public by default
struct Point {
    double x;  // public
    double y;  // public
};

// class: members are private by default
class BankAccount {
    std::string owner;   // private
    double balance;      // private

public:
    BankAccount(const std::string& name, double initial)
        : owner{name}, balance{initial} {}

    double get_balance() const { return balance; }

    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
};

int main() {
    Point p{3.0, 4.0};  // aggregate initialization
    std::cout << p.x << ", " << p.y << '\n';

    BankAccount acct{"Alice", 1000.0};
    acct.deposit(500.0);
    std::cout << acct.get_balance() << '\n';  // 1500
    // acct.balance = -999;  // ERROR: private member

    return 0;
}

محددات الوصول وهذا المؤشر

يوفر C++ ثلاثة مستويات وصول: public (يمكن لأي شخص الوصول)، private (فقط الفصل الدراسي وأصدقاؤه)، و protected (الفصل الدراسي وأصدقاؤه والفئات المشتقة). مؤشرthisهو مؤشر ضمني للكائن الحالي المتوفر داخل كل وظيفة عضو غير ثابتة.

access_specifiers.cpp
#include <iostream>
#include <string>

class Employee {
private:
    std::string name_;
    int id_;

protected:
    double salary_;  // accessible to derived classes

public:
    Employee(const std::string& name, int id, double salary)
        : name_{name}, id_{id}, salary_{salary} {}

    // const member function: promises not to modify the object
    const std::string& get_name() const { return name_; }
    int get_id() const { return id_; }

    // 'this' pointer usage — return *this for method chaining
    Employee& set_name(const std::string& name) {
        this->name_ = name;  // 'this->' disambiguates
        return *this;
    }

    Employee& set_id(int id) {
        this->id_ = id;
        return *this;
    }

    void print() const {
        std::cout << "Employee{" << name_ << ", #" << id_
                  << ", $" << salary_ << "}\n";
    }
};

int main() {
    Employee e{"Bob", 42, 75000.0};
    e.set_name("Robert").set_id(43);  // method chaining via *this
    e.print();  // Employee{Robert, #43, $75000}
    return 0;
}

الأعضاء الثابتة والصديق

الأعضاء الثابتون ينتمون إلى الفصل، وليس إلى أي مثيل. استخدمها لوظائف الحالة أو الأداة المساعدة المشتركة. الكلمة الأساسية friend تمنح وظيفة غير عضو أو فئة أخرى حق الوصول إلى الأعضاء الخاصين - استخدمها باعتدال، لأنها تكسر التغليف.

static_friend.cpp
#include <iostream>

class Widget {
    int value_;

    // C++17: inline static — define in header, no .cpp needed
    inline static int count_ = 0;

public:
    explicit Widget(int v) : value_{v} { ++count_; }
    ~Widget() { --count_; }

    static int get_count() { return count_; }

    // friend function can access private members
    friend std::ostream& operator<<(std::ostream& os, const Widget& w);
};

std::ostream& operator<<(std::ostream& os, const Widget& w) {
    return os << "Widget(" << w.value_ << ')';  // accesses private value_
}

int main() {
    Widget a{10}, b{20};
    std::cout << Widget::get_count() << '\n';  // 2
    std::cout << a << '\n';  // Widget(10)
    {
        Widget c{30};
        std::cout << Widget::get_count() << '\n';  // 3
    }
    std::cout << Widget::get_count() << '\n';  // 2
    return 0;
}

التهيئة المجمعة والمهيئات المعينة (C++20)

التجميع عبارة عن فئة أو بنية لا تحتوي على مُنشئات معلنة من قبل المستخدم، ولا يوجد أعضاء بيانات غير ثابتة خاصة/محمية، ولا توجد وظائف افتراضية، ولا فئات أساسية افتراضية. يضيف C++20 المهيئات المعينة، مما يتيح لك تسمية الأعضاء الذين تقوم بتهيئتهم.

designated_init.cpp
#include <iostream>
#include <string>

struct Config {
    std::string host = "localhost";
    int port = 8080;
    bool use_tls = false;
    int max_connections = 100;
};

int main() {
    // Traditional aggregate initialization
    Config c1{"example.com", 443, true, 200};

    // C++20 designated initializers — much more readable
    Config c2{
        .host = "api.example.com",
        .port = 443,
        .use_tls = true
        // .max_connections uses default value 100
    };

    std::cout << c2.host << ':' << c2.port << '\n';
    std::cout << "TLS: " << std::boolalpha << c2.use_tls << '\n';
    std::cout << "Max: " << c2.max_connections << '\n';  // 100
    return 0;
}
أفضل ممارسة

اجعل أعضاء البيانات خاصة بشكل افتراضي واكشف فقط ما هو مطلوب من خلال واجهة عامة بسيطة. إذا لم يكن للفئة أي متغير للمحافظة عليه (إنها مجرد حزمة من البيانات)، فاستخدم struct مع الأعضاء العامين بدلاً من ذلك. لا تقم بإضافة حروف الحروف والمحددات لكل حقل - فهذا يتعارض مع غرض التغليف. بدلاً من ذلك، قم بتوفير عمليات ذات معنى مثلdeposit()وwithdraw()بدلاً منset_balance().

خطأ شائع

إذا لم تقم وظيفة العضو بتعديل حالة الكائن، فقم بوضع علامة عليهاconst. بدونconst، لا يمكنك استدعاء الوظيفة على كائنconstأو من خلال مرجعconst- وهو ما يحدث طوال الوقت عند تمرير الكائنات بواسطةconst&. يعدconstالمفقود على أحد الملحقات مصدرًا شائعًا لأخطاء الترجمة في قواعد التعليمات البرمجية الأكبر حجمًا.

النقاط الرئيسية
  • يختلفstructوclassفقط في الوصول الافتراضي:structعام، وclassخاص
  • يحمي التغليف ثوابت الفئة — استخدم بياناتprivateمع واجهة عامة ذات معنى
  • قم بوضع علامة على وظائف الأعضاء غير المتحولةconstبحيث تعمل مع مراجعconst
  • استخدمinline static(C++17) للأعضاء الثابتين المحددين في الرؤوس
  • أدوات التهيئة المعينة لـ C++20 تجعل التهيئة الإجمالية قابلة للقراءة وتوثيقًا ذاتيًا
  • يجب استخدامfriendباعتدال — عادةً للأحمال الزائدة للمشغل التي تحتاج إلى وصول خاص

اختبار — اختبر معرفتك

(15 نقطة خبرة)

1. ما هو الفرق الوحيد بين`struct`و`class`في C++؟

2. لماذا يجب عليك وضع علامة على وظائف الأعضاء الملحقين على أنها`const`؟

3. ما الذي يحققه`inline static int count_ = 0;`في C++17؟