Αντικειμενοστρεφής προγραμματισμός στη C: Διαφορά μεταξύ των αναθεωρήσεων

Περιεχόμενο που διαγράφηκε Περιεχόμενο που προστέθηκε
μ σύνδεσμοι
Γραμμή 1:
Η βασική διαφορά του [[Αντικειμενοστρεφής προγραμματισμός|αντικειμενοστρεφούς προγραμματισμού]] από τον [[Διαδικαστικός προγραμματισμός|διαδικαστικό προγραμματισμό]] είναι η συσχέτιση των [[Δεδομένα|δεδομένων]] με τις λειτουργίες που μπορούν να
δεχθούν/πραγματοποιήσουν. Η συσχέτιση αυτή πραγματοποιείται με μία από τις βασικές μονάδες του αντικειμενοστρεφούς προγραμματισμού, την ''κλάση''.
προγραμματισμό είναι η συσχέτιση των δεδομένων με τις λειτουργίες που μπορούν να
 
δεχθούν/πραγματοποιήσουν. Η συσχέτιση αυτή πραγματοποιείται με μία από τις βασικές
Αν και η [[γλώσσα προγραμματισμού]] [[C είναι(γλώσσα μίαπρογραμματισμού)|C]] είναι πολύ δυνατή γλώσσαισχυρή, με αυξημένη δυνατότητα ελέγχου του [[Υλικό υπολογιστών|υλικού]] και των παραμέτρων αυτού, δεν είναι αντικειμενοστρεφής. Εντούτοις, και με την χρήση των δυνατοτήτων της γλώσσας, μπορεί να προσομοιωθεί η λειτουργικότητα που προσφέρουν οι αντικειμενοστρεφείς γλώσσες και ο αντικειμενοστρεφής προγραμματισμός.
μονάδες του αντικειμενοστρεφούς προγραμματισμού, την Κλάση.
 
Αν και η γλώσσα C είναι μία πολύ δυνατή γλώσσα, με αυξημένη δυνατότητα ελέγχου του υλικού και των παραμέτρων αυτού, δεν είναι αντικειμενοστρεφής. Εντούτοις, και με την χρήση των δυνατοτήτων της γλώσσας, μπορεί να προσομοιωθεί η λειτουργικότητα
που προσφέρουν οι αντικειμενοστρεφείς γλώσσες και ο αντικειμενοστρεφής
προγραμματισμός.
Σε αυτό το άρθρο, θα παρουσιαστούν οι τρόποι με τους οποίους μπορούν να προσομοιωθούν οι εξής λειτουργίες:
* Κληρονομικότητα
* [[Πολυμορφισμός]]
* Πρότυπα
 
Μέσα από τις προαναφερθείσες λειτουργίες θα αναδειχθούν οι τρόποι και οι δυνατότητες της γλώσσας C με τους οποίους μπορεί να προσομοιώσει τις ανεπτυγμένεςαναπτυγμένες λειτουργίες των αντικειμενοστρεφών γλωσσών.
 
==Πολυμορφισμός στην C==
 
Ο πολυμορφισμός είναι μία από τις πιο σημαντικές έννοιες στον αντικειμενοστρεφή προγραμματισμό. Αν κοιτάξουμε όλα τα αρχικά πρότυπα σχεδίασης, σχεδόν όλα τους, χρησιμοποιούν τον πολυμορφισμό και τις ιδιότητές του.
 
===Μέθοδοι Constructor και Destructor===
Στην [[C++]], οι δύο αυτές μέθοδοι είναι κάποιες ειδικές μέθοδοι που καλούνται όταν ένα αντικείμενο δημιουργείται ή καταστρέφεται αντιστοίχως. Ο χειριστής new κατανέμει την μνήμη για την κλάση και μετά καλεί τον κατασκευαστή (Constructor) της κλάσης αυτής. Αντίστοιχα, ο χειριστής delete καλεί πρώτα τον Destructor της κλάσης και μετά αποδεσμεύει την μνήμη που χρησιμοποιούσε η κλάση.
 
===Διάταξη μνήμης ενός αντικειμένου===
Στην C, πρέπει να δημιουργήσουμε δύο μεθόδους για κάθε κλάση. Μία για τον Constructor και μία για τον Destructor. Το όνομα της μεθόδου του Constructor θα είναι το όνομα της κλάσης μαζί με το _Ctor και το όνομα της μεθόδου του Destructor θα είναι το όνομα της κλάσης μαζί με το _Dtor (π.χ. για την κλάση X, θα έχουμε: _CtorX_Ctor και _DtorX_Dtor).
Στην C++, όταν δημιουργείται ένα αντικείμενο, κατανέμεται μνήμη μόνο για τα δεδομένα της κλάσης. Υπάρχει μόνο ένα αντίγραφο των μεθόδων μελών και διαμοιράζονται σε όλα τα στιγμιότυπα της κλάσης.
 
Στην C, για να υλοποιήσουμε αυτή την συμπεριφορά, δημιουργούμε όλες τις μεθόδους, όλων των κλάσεων ως καθολικές (global )μεθόδους (αυτό συμπεριλαμβάνει και τις μεθόδους Constructor και Destructor). Τα ονόματα των μεθόδων θα έχουν στην αρχή το όνομα της κλάσης στην οποία ανήκουν και μετά underscore και το όνομά τους (π.χ. για την μέθοδο Two της κλάσης X, θα είναι _TwoX_Two). Επιπλέον είμαστε υποχρεωμένοι να βάλουμε τα δεδομένα της κλάσης σε μία [[Εγγραφή (γλώσσες προγραμματισμού)|δομή]] (structure) της C καθότι η λέξη class δεν είναι διαθέσιμη στην C.
 
Στην C++, μόνο ένα αντίγραφο των μεθόδων μελών υπάρχει στην μνήμη και για να διακρίνουμε τα στιγμιότυπα των κλάσεων μεταξύ τους, χρησιμοποιούμε τον δείκτη this, ο οποίος δείχνει κάθε στιγμή στο στιγμιότυπο που δημιουργήθηκε. Οι μέθοδοι χρησιμοποιούν τον δείκτη this για να αποκτούν πρόσβαση στα δεδομένα. Εσωτερικά, η διεύθυνση στον δείκτη this δίνεται στις μεθόδους μέλη μέσα από τον [[καταχωρητής|καταχωρητή]] ecx του [[μικροεπεξεργαστής|μικροεπεξεργαστή]].
Εσωτερικά, η διεύθυνση στον δείκτη this δίνεται στις μεθόδους μέλη μέσα από τον καταχωρητή ecx του μικροεπεξεργαστή.
 
Στην C, για να υλοποιήσουμε τον δείκτη this, δηλώνουμε μία μεταβλητή global integer που ονομάζουμε ECX. Πριν καλέσουμε οποιαδήποτε μέθοδο, η μεταβλητή ECX θα τεθεί ώστε να δείχνει στην διεύθυνση μνήμης που χρησιμοποιεί η δομή δεδομένων μας. Όλες οι μέθοδοι χρησιμοποιούν, έτσι, την μεταβλητή ECX ώστε να αποκτήσουν πρόσβαση στα μέλη της δομής.
 
===Εικονικός πίνακας και δείκτης εικονικού πίνακα===
Στην C++, κάθε κλάση που έχει τουλάχιστον μία εικονική μέθοδο, θα έχει έναν σχετικό εικονικό πίνακα. Δεν πρόκειται για τίποτα άλλο παρά για ένα πίνακα με δείκτες μεθόδων. Αυτός ο πίνακας για μία κλάση περιέχει τις διευθύνσεις των εικονικών μεθόδων που περιέχονται στην κλάση καθώς και τις διευθύνσεις των εικονικών μεθόδων που κληρονομεί η κλάση. Στην περίπτωση των κληρονομούμενων μεθόδων, λαμβάνονται υπ' όψιν μόνο αυτές που δεν υπερκαλύπτονται. (Function Override)
 
Η σειρά με την οποία συμπληρώνεται ο εικονικός πίνακας έχει ως εξής:
Γραμμή 42 ⟶ 39 :
#Οι διευθύνσεις των νέων εικονικών μεθόδων που δηλώνονται στην κλάση προστίθεται, βάσει της σειράς δήλωσης.
Στην C, θα υλοποιήσουμε τις εικονικές μεθόδους ως global πίνακες void δεικτών void και θα γεμίσουμε τον πίνακα με τις διευθύνσεις των εικονικών μεθόδων της κλάσης. Στην περίπτωση μας, υπάρχουν τρεις κλάσεις με εικονικές μεθόδους και έτσι, θα υπάρχουν τρεις εικονικοί πίνακες.
 
Ο εικονικός πίνακας για την κλάση X θα περιέχει τις διευθύνσεις του Destructor και τις διευθύνσεις των τριών εικονικών μεθόδων.
Γραμμή 124 ⟶ 121 :
===Παράδειγμα===
Θα δημιουργήσουμε μία κλάση "Point" η οποία αντιπροσωπεύει ένα σημείο στο δισδιάστατο χώρο. Η κλάση αυτή θα περιέχει δύο ακέραιες μεταβλητές, οι οποίες αντιπροσωπεύουν τις συντεταγμένες του σημείου. Θα παρέχουμε επίσης τη δυνατότητα δημιουργίας και καταστροφής του αντικειμένου καθώς επίσης και τη δυνατότητα μετακίνησής του. Θέλουμε επίσης και την κλάση "Circle" η οποία θα αντιπροσωπεύει ένα κύκλο. Η κλάση αυτή θα αποτελείται απ ο ένα σημείο καθώς επίσης και μία επιπλέον μεταβλητή η οποία θα αποτελεί την ακτίνα του κύκλου και η κλάση αυτή θα παρέχει μία μέθοδο δημιουργίας και διαγραφής καθώς επίσης και μία μέθοδο μετακίνησης.
 
====Η κλάση "Class"====
Θέλουμε να κατασκευάσουμε την κλάση "Point" με τέτοιο τρόπο ώστε η κλάση "Circle" να μπορεί να κληρονομήσει απ ο αυτή. Κάθε κλάση στον αντικειμενοστραφή προγραμματισμό έχει κάποια κοινά χαρακτηριστικά με τις υπόλοιπες κλάσεις (κάθε κλάση έχει ένα κατασκευαστή), για να διατηρήσουμε μία συνοχή με τις αντικειμενοστραφής γλώσσες θα πρέπει να παρέχουμε μία παρόμοια διεπαφή. Καθώς το να παρέχουμε ένα γενικό τρόπο κατασκευής και δημιουργίας αντικειμένων προσθέτει αρκετά προβλήματα, κάνει τον κώδικα επιρρεπή σε λάθη και περιορίζει τις δυνατότητες θα πρέπει το κάθε αντικείμενο να γνωρίζει τη πόρους χρειάζεται καθώς και πώς να τους ελευθερώσει. Έτσι μπορούμε να χρησιμοποιούμε τη γενική συνάρτηση "new()" η οποία θα αναλαμβάνει να δημιουργεί τα αντικείμενα και η συνάρτηση "delete()" η οποία θα καταστρέφει το αντικείμενο.
Γραμμή 150 ⟶ 148 :
}
</source>
 
Η συνάρτηση "new()" δημιουργεί το νέο αντικείμενο και επιστρέφει ένα δείκτη σε αυτό. Μετά τη δημιουργία του αντικειμένου η μεταβλητή "p" δείχνει στο νέο αντικείμενο και ο δείκτης "class" του αντικειμένου "δείχνει" στην αρχή του αντικειμένου. Εάν υπάρχει κατασκευαστής για το αντικείμενο τότε καλούμε τον κατασκευαστή και επιστρέφουμε το αποτέλεσμά του, δηλαδή το νέο αντικείμενο.
 
Η συνάρτηση "delete()" διαγράφει το αντικείμενο καλώντας τη συνάρτηση καταστροφής του αντικειμένου, η οποία είναι υπεύθυνη για την απελευθέρωση τον πόρων που έχει δεσμεύσει.
Γραμμή 161:
}
</source>
 
====Η κλάση "Point"====
Μπορούμε να χρησιμοποιήσουμε την κλάση "Class" προσθέτοντας ένα δείκτη προς τη μέθοδο void (* draw) (const void * self) η οποία θα σχεδιάζει το σημείο.
Γραμμή 182 ⟶ 183 :
</source>
Η συνάρτηση "move()" δεν χρειάζεται να "συνδεθεί" με την κλάση καθώς η μεταφορά του σημείου και του κύκλου είναι ίδια καθώς ο κύκλος αποτελείται από ένα σημείο και την ακτίνα.
 
====Η κλάση "Circle"====
Η κλάση "Circle" αποτελείται απ ο ένα σημείο και την ακτίνα άρα η κλάση "Circle" γίνεται:
Γραμμή 207 ⟶ 209 :
Τα πρότυπα είναι μία αρκετά εύχρηστη λειτουργία των αντικειμενοστρεφών γλωσσών με την οποία μπορούν να δημιουργηθούν αλγόριθμοι και αφηρημένες μέθοδοι για να προσδιοριστούν έπειτα αναλόγως με τους τύπους δεδομένων που θα συσχετιστούν.
 
Εν γένει, τα πρότυπα είναι λειτουργία [[μεταπρογραμματισμός|μεταπρογραμματισμού]] (metaprogramming)<ref name="metaprogramming">[http://en.wikipedia.org/wiki/Metaprogramming Metaprogramming]</ref>, χρησιμοποιούμενη από αρκετές γλώσσες μεταξύ των οποίων και η C++, με την οποία επιτρέπεται η δημιουργία ενός γενικού αλγόριθμου ο οποίος μπορεί να χρησιμοποιηθεί όταν συσχετιστεί με κάποιο τύπο δεδομένων και για οποιοδήποτε τύπο δεδομένων.
Η πιο συνηθισμένη χρήση των templates είναι για την υλοποίηση δομών δεδομένων(Containers) οι οποίες μπορούν να χρησιμοποιηθούν για οποιοδήποτε τύπο δεδομένων. Έτσι, ο προγραμματιστής μπορεί να χρησιμοποιήσει έτοιμο αλγόριθμο, όπως η λειτουργία μίας λίστας, με διαφορετικούς τύπους δεδομένων αρχικοποιώντας κάθε φορά την δομή αυτή με τον τύπο δεδομένων που επιθυμεί. Ένα παράδειγμα είναι η κλάση CAtlArray, της βιβλιοθήκης atlcoll.h, μέλος της γενικής βιβλιοθήκης STL<ref name="atl">[http://en.wikipedia.org/wiki/Standard_Template_Library[C++ StandardΠρότυπη Template Libraryβιβλιοθήκη|STL]]</ref>. Ενδεικτικά, για τη λειτουργία της αρχικοποίησης.:
<source lang="C">
Γραμμή 225 ⟶ 227 :
===Πρότυπα στην C===
Στη C τα πρότυπα (templates), μπορούν να προσομοιωθούν με την χρήση των μακροεντολών (macros) που προσφέρει ο προ-επεξεργαστής[[προεπεξεργαστής]] (preprocessor). Έτσι, την `«αντιληπτική`» ικανότητα των προτύπων να αρχικοποιούνται και να συμπεριφέρονται αναλόγως με τον τύπο δεδομένων την προσομοιώνει η C με την χρήση των μακροεντολών, όπου παράγεται κώδικας με την διαχείριση και αντικατάσταση κειμένου που γράφεται στις μακροεντολές κατά το χρόνο μεταγλώτισσης.
Μία από τις πιο έντονες χρήσεις της προσομοίωσης των templates απαντάται στον κώδικα του [[Πυρήνας Linux|πυρήνα του Linux]], όπου έχουν υλοποιηθεί δομές όπως η λίστα, η ούρα κτλ., και μπορεί να βρεθεί στην βιβλιοθήκη sys/sys/queue.h<ref name="queue"?[http://fxr.watson.org/fxr/source/sys/queue.h ]</ref>.
 
Αντιγράφοντας από τον προαναφερθέντα κώδικα μπορεί κανείς να παρατηρήσει την τη διαχείριση της αντικατάστασης κειμένου για την παραγωγή κώδικα με σκοπό την δημιουργία ενός στοιχείου της λίστας καθώς και της κεφαλής της λίστας:
 
<source lang="C">
Γραμμή 254 ⟶ 256 :
</source>
Έτσι, ο prepocessorπροεπεξεργαστής της C θα αντικαταστήσει το όνομα του struct με το όνομα myListHead και τον τύπο με το κείμενο int και θα γίνει η κλήση της εντολής σαν να την είχε γράψει ο προγραμματιστής. Επίσης, θα κληθεί η μακροεντολή SLIST_HEAD_INITIALIZER όπου θα αντικαταστήσει το κείμενο και θα αποδώσει την τιμή NULL στην μεταβλητή myHead.
Το ίδιο μπορεί να γίνει και για την λειτουργικότητα που μπορεί να προσφέρει μία λίστα. Π.χ.,
Γραμμή 265 ⟶ 267 :
(var) = SLIST_NEXT((var), field))
</source>
Όπου, και πάλι, μπορεί να φανεί η διαχείριση του κειμένου από τον prepocessor για την παραγωγή κώδικα και την προσομοίωση της λειτουργικότητας των προτύπων. Έτσι, για την τη λειτουργία foreach ο preprocessorπροεπεξεργαστής:
 
#θα αποδώσει την διεύθυνση μνήμης της κεφαλής της λίστας στην μεταβλητή ,
#θα ελέγξει εάν η τιμή είναι NULL ,
#στην επανάληψη θα αποδώσει στην μεταβλητή var την τιμή του επόμενου πεδίου της λίστας.
 
==Παραπομπές==
<references/>