Μετάβαση στο περιεχόμενο

Classes, Structs & Encapsulation

Understand the difference between struct and class, access specifiers, encapsulation, const member functions, static members, and modern aggregate initialization.

What Is a Class?

Μια κλάση στο C++ είναι ένας τύπος που ορίζεται από το χρήστη που ομαδοποιεί δεδομένα (μεταβλητές μέλους) και λειτουργίες σε αυτά τα δεδομένα (συναρτήσεις μέλους) σε μια ενιαία μονάδα. Αυτή η ομαδοποίηση ονομάζεται ενθυλάκωση — ένας από τους τέσσερις πυλώνες του OOP.

Η βασική ιδέα είναι ότι μια κλάση ορίζει μια αμετάβλητη: ένα σύνολο κανόνων που πρέπει πάντα να ισχύουν για την εσωτερική της κατάσταση. Για παράδειγμα, μια κλάσηDateμπορεί να απαιτεί ο μήνας να είναι πάντα μεταξύ 1 και 12. Η ενθυλάκωση σάς επιτρέπει να επιβάλλετε αυτά τα αμετάβλητα ελέγχοντας την πρόσβαση στην εσωτερική κατάσταση.

struct vs class

Στο 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;
}

Access Specifiers & this Pointer

Το 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 με δημόσια μέλη. Μην προσθέτετε getters και setters για κάθε πεδίο — αυτό ακυρώνει τον σκοπό της ενθυλάκωσης. Αντίθετα, παρέχετε σημαντικές λειτουργίες όπωςdeposit()καιwithdraw()αντίset_balance().

Παγίδα

Εάν μια συνάρτηση μέλους δεν τροποποιεί την κατάσταση του αντικειμένου, σημειώστε την ωςconst. Χωρίςconst, δεν μπορείτε να καλέσετε τη συνάρτηση σε ένα αντικείμενοconstή μέσω μιας αναφοράςconst— κάτι που συμβαίνει συνεχώς όταν διαβιβάζετε αντικείμενα από τοconst&. Έναconstπου λείπει σε ένα Accessor είναι μια κοινή πηγή σφαλμάτων μεταγλώττισης σε μεγαλύτερες βάσεις κωδικών.

Βασικά συμπεράσματα
  • Τοstructκαι τοclassδιαφέρουν μόνο στην προεπιλεγμένη πρόσβαση: τοstructείναι δημόσιο, τοclassείναι ιδιωτικό
  • Η ενθυλάκωση προστατεύει αμετάβλητα κλάσης — χρησιμοποιήστε δεδομέναprivateμε μια ουσιαστική δημόσια διεπαφή
  • Επισημάνετε τις μη μεταλλαγμένες συναρτήσεις μέλουςconstώστε να λειτουργούν με αναφορέςconst
  • Χρησιμοποιήστε τοinline static(C++17) για στατικά μέλη που ορίζονται στις κεφαλίδες
  • Οι καθορισμένοι αρχικοποιητές C++20 καθιστούν την εκκίνηση συγκεντρωτικών αναγνώσιμων και αυτο-τεκμηριωμένων
  • Τοfriendθα πρέπει να χρησιμοποιείται με φειδώ — συνήθως για υπερφορτώσεις χειριστή που χρειάζονται ιδιωτική πρόσβαση

Κουίζ — Δοκιμάστε τις γνώσεις σας

(15 XP)

1. Ποια είναι η ΜΟΝΗ διαφορά μεταξύ`struct`και`class`στο C++;

2. Γιατί πρέπει να επισημάνετε τις λειτουργίες μέλους πρόσβασης ως`const`;

3. Τι επιτυγχάνει το`inline static int count_ = 0;`στο C++17;