| Both sides previous revisionPrevious revisionNext revision | Previous revision |
| cpp:casting [2026/04/24 12:09] – [Παράδειγμα 2ο: Κληρονομικότητα (Upcasting & Downcasting)] gthanos | cpp:casting [2026/04/24 12:28] (current) – [reinterpret_cast<>] gthanos |
|---|
| Το **static_cast** εφαρμόζεται σχεδόν στα πάντα: | Το **static_cast** εφαρμόζεται σχεδόν στα πάντα: |
| |
| * Κανονικούς Τύπους: Μπορείς να μετατρέψεις βασικούς τύπους μεταξύ τους (π.χ. double σε int, float σε char). | * **Κανονικούς Τύπους:** Μπορείς να μετατρέψεις βασικούς τύπους μεταξύ τους (π.χ. double σε int, float σε char). |
| * Pointers: Μετατροπή μεταξύ δεικτών σε μια ιεραρχία κλάσεων (Base* σε Derived* και αντίστροφα). | * **Pointers:** Μετατροπή μεταξύ δεικτών σε μια ιεραρχία κλάσεων (Base* σε Derived* και αντίστροφα). |
| * References: Λειτουργεί ακριβώς όπως και με τους pointers για αναφορές σε κλάσεις. | * **References**: Λειτουργεί ακριβώς όπως και με τους pointers για αναφορές σε κλάσεις. |
| * Enums: Μετατροπή enums σε integers και αντίστροφα. | * **Enums:** Μετατροπή enums σε integers και αντίστροφα. |
| |
| **Σημαντικό:** Στο **static_cast**, αν η μετατροπή αναφορών (references) αποτύχει λογικά (π.χ. το αντικείμενο δεν είναι αυτό που νομίζεις), το πρόγραμμα θα συνεχίσει να τρέχει με λάθος δεδομένα (undefined behavior), γιατί δεν υπάρχει έλεγχος στο runtime. | **Σημαντικό:** Στο **static_cast**, αν η μετατροπή αναφορών (references) αποτύχει λογικά (π.χ. το αντικείμενο δεν είναι αυτό που νομίζεις), το πρόγραμμα θα συνεχίσει να τρέχει με λάθος δεδομένα (undefined behavior), γιατί δεν υπάρχει έλεγχος στο runtime. |
| </WRAP> | </WRAP> |
| |
| ====== dynamic_cast<> ====== | ===== dynamic_cast<> ===== |
| |
| Το **dynamic_cast** είναι ο τύπος casting που εφαρμόζεται στον πολυμορφισμό. Το χρησιμοποιούμε όταν έχουμε έναν δείκτη προς μια βασική κλάση (Base*) και εξετάζουμε αν το αντικείμενο στο οποίο δείχνει είναι μια συγκεκριμένη παράγωγη κλάση (Derived*). | Το **dynamic_cast** είναι ο τύπος casting που εφαρμόζεται στον πολυμορφισμό. Το χρησιμοποιούμε όταν έχουμε έναν δείκτη προς μια βασική κλάση (Base*) και εξετάζουμε αν το αντικείμενο στο οποίο δείχνει είναι μια συγκεκριμένη παράγωγη κλάση (Derived*). |
| </code> | </code> |
| |
| ==== Παρατηρήσεις ==== | <WRAP tip 80% center round> |
| |
| * **Runtime Check:** Το dynamic_cast χρησιμοποιεί μια πληροφορία που ονομάζεται RTTI (Run-Time Type Information). Όταν καλείται η processAccount, ο compiler δεν ξέρει τι περιέχει το acc. Η απόφαση παίρνεται την ώρα που τρέχει το πρόγραμμα. | * **Runtime Check:** Το dynamic_cast χρησιμοποιεί μια πληροφορία που ονομάζεται RTTI (Run-Time Type Information). Όταν καλείται η συνάρτηση processAccount, ο compiler δεν ξέρει τι περιέχει η μεταβλητή acc. Η απόφαση παίρνεται την ώρα που εκτελείται το πρόγραμμα. |
| * **Ασφάλεια (Safety):** Αν χρησιμοποιούσαμε **static_cast** και το αντικείμενο ήταν CheckingAccount, ο pointer savings θα ήταν έγκυρος (αλλά θα έδειχνε σε λάθος δεδομένα). Η κλήση savings->applyInterest() θα οδηγούσε σε crash. Το dynamic_cast μας επιστρέφει nullptr και μας σώζει. | * **Ασφάλεια (Safety):** Αν χρησιμοποιούσαμε **static_cast** και το αντικείμενο ήταν τύπου CheckingAccount, ο pointer savings θα ήταν έγκυρος (αλλά θα έδειχνε σε λάθος δεδομένα). Η κλήση savings->applyInterest() θα οδηγούσε σε crash. Το dynamic_cast μας επιστρέφει **nullptr** και μας σώζει. |
| * **Πολυμορφισμός:** Πρόσεξε ότι η Account έχει virtual συναρτήσεις. Χωρίς αυτές, το dynamic_cast θα έβγαζε σφάλμα στο compilation, γιατί δεν θα υπήρχε πίνακας vtable για να ελέγξει τον τύπο. | * **Πολυμορφισμός:** Πρόσεξε ότι η Account έχει //virtual// συναρτήσεις. Χωρίς αυτές, το dynamic_cast θα έβγαζε σφάλμα στη μεταγλώττιση, γιατί δεν θα υπήρχε πίνακας vtable για να ελέγξει τον τύπο. |
| | * **Δε λειτουργεί για κανονικούς τύπους:** Δεν μπορείς να κάνεις dynamic_cast<int>(my_double). Θα πάρεις σφάλμα κατά τη μεταγλώττιση. Το **dynamic_cast** απαιτεί ο τύπος προορισμού να είναι δείκτης ή αναφορά σε τύπο κλάσης. |
| |
| | </WRAP> |
| |
| | **Συνοπτικός Πίνακας** |
| |
| | ^ Τύπος ^ static_cast ^ dynamic_cast ^ |
| | ^ Βασικοί Τύποι (int, double, κλπ) | Ναι | Όχι | |
| | ^ Pointers (Base* -> Derived*) | Ναι (χωρίς check) | Ναι (επιστρέφει nullptr σε αποτυχία) | |
| | ^ References (Base& -> Derived&) | Ναι (χωρίς check) | Ναι (πετάει std::bad_cast σε αποτυχία) | |
| | ^ Enums | Ναι | Όχι | |
| |
| ====== const_cast<> ====== | |
| | ===== const_cast<> ===== |
| | |
| | Είναι ο μόνος τελεστής που μπορεί να προσθέσει ή να αφαιρέσει το const (ή το volatile) από μια μεταβλητή. |
| | |
| | * **Γιατί να το κάνεις:** Συνήθως χρησιμοποιείται όταν έχεις να κάνεις με παλιές βιβλιοθήκες (Legacy C code) που δέχονται char* ενώ ξέρεις ότι δεν πρόκειται να τροποποιήσουν το περιεχόμενο, αλλά εσύ έχεις ένα const char*. |
| | * **Ο Κίνδυνος:** Αν αφαιρέσεις το const από μια μεταβλητή που ορίστηκε εξ αρχής ως const και προσπαθήσεις να την αλλάξεις, το αποτέλεσμα είναι Undefined Behavior (μπορεί να "σκάσει" το πρόγραμμα ή να μην αλλάξει η τιμή ποτέ). |
| | |
| | <code cpp cost_cast.cpp> |
| | void legacy_function(char* str) { |
| | /* ... */ |
| | } |
| | |
| | const char* my_text = "Hello"; |
| | // legacy_function(my_text); // ΣΦΑΛΜΑ |
| | legacy_function(const_cast<char*>(my_text)); // Επιτρέπεται |
| | </code> |
| |
| ====== reinterpret_cast<> ====== | ====== reinterpret_cast<> ====== |
| | |
| | Αυτός είναι ο πιο ισχυρός και επικίνδυνος τελεστής. Λέει στον compiler: "Κάνε αυτή τη μετατροπή, ακόμα και αν δεν βγάζει νόημα". |
| | |
| | **Τι κάνει:** Μετατρέπει οποιονδήποτε δείκτη σε οποιονδήποτε άλλον τύπο δείκτη, ή ακόμα και δείκτη σε ακέραιο και το αντίστροφο. |
| | |
| | **Προσοχή:** Δεν ελέγχει τίποτα. Αν μετατρέψεις έναν int* σε Dog* και καλέσεις μια μέθοδο, ο compiler θα το κάνει και απλώς θα προσπαθήσει να διαβάσει το περιεχόμενο του int** σαν να ήταν αντικείμενο της κλάσης Dog. |
| | |
| | <code cpp reinterpret_cast.cpp> |
| | long address = 0x7FFF1234; |
| | // Ερμήνευσε αυτόν τον αριθμό ως διεύθυνση μνήμης ενός ακεραίου |
| | int* p = reinterpret_cast<int*>(address); |
| | </code> |
| | |
| | Ο συγκεκριμένος τελεστής μοιάζει με το type casting που γνωρίζουμε από τη γλώσσα C, διότι ο μεταγλωττιστής ακολουθεί χωρίς έλεγχο τις οδηγίες του προγραμματιστή. |
| | |
| | |