Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων Αντικειμενοστραφής Προγραμματισμός
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 2 Εισαγωγή * Στόχος είναι η διαίρεση ενός προγράμματος σε αρχεία τα οποία μεταγλωττίζονται ξεχωριστά. * Σε πρώτη φάση δημιουργούμε ξεχωριστά αρχεία για κάθε κλάση και ξεχωριστά για τα προγράμματα που χρησιμοποιούν τις κλάσεις. * Με αυτόν τον τρόπο μπορώ να κρατώ μεταγλωττισμένα όλα τα ξεχωριστά αρχεία και αν προκύψουν αλλαγές σε μία κλάση ή κάποιο driver πρόγραμμα να μη χρειάζεται να μεταγλωτίσσω όλον τον κώδικα από την αρχή, παρά μόνο το αρχείο που άλλαξε.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 3 Εισαγωγή * Τα ξεχωριστά μεταγλωττισμένα κομμάτια κώδικα συνδέονται μεταξύ τους (linking phase) πριν την εκτέλεση. * Σε δεύτερη φάση σπάμε το αρχείο μιας κλάσης συνήθως σε 2 αρχεία, ένα που περιέχει τη διασύνδεση της κλάσης και ένα που περιέχει την υλοποίηση. * Έτσι μπορούμε να δημιουργήσουμε βιβλιοθήκες κλάσεων όπως ακριβώς οι βιβλιοθήκες που έχουμε χρησιμοποιήσει ως τώρα.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 4 Διασύνδεση και Δήλωση Κλάσης * Σύμφωνα με την αρχή της ενθυλάκωσης στη διασύνδεση μιας κλάσης περιλαμβάνονται μόνο οι δηλώσεις των δημόσιων μελών. * Κανονικά λοιπόν θα έπρεπε τα δημόσια μέλη να μπουν σε ξεχωριστό αρχείο (αρχείο διασύνδεσης) και τα ιδιωτικά μαζί με τους ορισμούς των συναρτήσεων μελών σε άλλο (αρχείο υλοποίησης). * Η δήλωση μιας κλάσης όμως πρέπει να είναι ενιαία και όχι σε 2 αρχεία. * Για το λόγο αυτό στο αρχείο διασύνδεσης μπαίνει όλη η δήλωση της κλάσης (μαζί με τα ιδιωτικά μέλη στα οποία ο χρήστης δεν έχει πρόσβαση ούτως ή άλλως) και στο αρχείο υλοποίησης οι ορισμοί των συναρτήσεων μελών ή φίλιων.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 5 Αρχεία Κεφαλίδων * Αρχείο διασύνδεσης = Αρχείο Κεφαλίδας (header file) * Τα header files πρέπει να έχουν κατάληξη.h (πχ. dtime.h) * Κάθε πρόγραμμα που χρησιμοποιεί την κλάση που δηλώνεται στο αρχείο dtime.h πρέπει να περιέχει μια οδηγία # include “dtime.h” (δες DigitalTime κλάση) * Τα “ ” οδηγούν τον compiler να ψάξει για τον ορισμό του dtime.h στον τρέχοντα κατάλογο ή σε όποιον κατάλογο έχει οριστεί να σώζει τα header files που δημιουργούμε. * Αντίθετα τα <> οδηγούν τον compiler να ψάξει στον κατάλογο που σώζει τις προκαθορισμένες βιβλιοθήκες.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 6 Αρχείο Υλοποίησης * Περιέχει τους ορισμούς των συναρτήσεων μελών. * Συνήθως δίνεται το ίδιο όνομα με αυτό της κεφαλίδας με διαφορετική κατάληξη όμως. Η κατάληξη εξαρτάται από τον compiler πχ. dtime.cpp. Κάνει include το αντίστοιχο header file include “dtime.h”
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 7 Αρχείο Εφαρμογής και Εκτέλεση * Περιέχει τη main ή γενικότερα συναρτήσεις που χρησιμοποιούν την κλάση. * Έχει κατάληξη.cpp (πχ. timedemo.cpp) Κάνει include το αντίστοιχο header file include “dtime.h” ΕΚΤΕΛΕΣΗ - Μεταγλωττίζουμε τα.cpp αρχεία ξεχωριστά. Τα.h δε χρειάζονται μεταγλώττιση αφού γίνονται include. - Κατόπιν χρησιμοποιώντας τον linker πρέπει να συνδεθούν οι ξεχωριστές μεταγλωττίσεις των.cpp. Ο ακριβής τρόπος ποικίλει. Στο UNIX χρησιμοποιείται το πρόγραμμα make για τη σύνδεση. Σε ολοκληρωμένα περιβάλλοντα ανάπτυξης (IDE) χρησιμοποιείται η έννοια του project.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 8 Πλεονεκτήματα Διαχωρισμού * Λιγότερος κώδικας. Αν πολλά προγράμματα εφαρμογής χρησιμοποιούν μια κλάση δε χρειάζεται να γράφετε τον ορισμό της κλάσης σε κάθε ένα από αυτά. Επιπλέον οι όποιες αλλαγές στην υλοποίηση της κλάσης επηρεάζουν ένα αρχείο και όχι όλα τα προγράμματα που τη χρησιμοποιούν. * Εξοικονόμηση χρόνου μεταγλώττισης.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 9 #ifndef - #define * Είδαμε παράδειγμα προγράμματος που έχει δομηθεί σε 3 αρχεία. Ένα πρόγραμμα μπορεί να περιλαμβάνει πολλά περισσότερα αρχεία (αν χρησιμοποιεί πολλές κλάσεις κλπ). * Ένα αρχείο επικεφαλίδας μπορεί να γίνεται include σε περισσότερα του ενός αρχεία που με τη σειρά τους μπορεί να κάνει include το ένα το άλλο. Εύκολα λοιπόν μπορεί να προκύψει το σενάριο όπου επαναλαμβάνονται οι δηλώσεις που περιέχονται σε ένα header file πολλές φορές μέσα στο κώδικα. * Μπορούμε εύκολα να αποφύγουμε πολλαπλούς ορισμούς με χρήση των #ifndef - #define
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 10 Παράδειγμα #ifndef DTIME_H #define DTIME_H #endif Αυτό που γίνεται είναι το εξής: - Την πρώτη φορά που γίνεται include το dtime.h το DTIME_H δεν είναι defined. - Κατά συνέπεια ο έλεγχος #ifndef (if not defined) είναι true και εκτελείται η #define DTIME_H μαζί με την αντιγραφή του dtime.h.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 11 Εξήγηση (συνέχεια) - Η #define το μόνο που κάνει είναι να προσθέτει το DTIME_H σε μια λίστα ονομάτων. - Τη δεύτερη φορά που θα γίνει include to dtime.h η #ifndef αποτυγχάνει (αφού κοιτώντας στη λίστα των ορισμών βλέπει το DTIME_H). - Όταν αποτύχει η #ifndef οτιδήποτε ακολουθεί μέχρι το επόμενο #endif παραλείπεται. - Κατά συνέπεια δε θα οριστεί παραπάνω από μία φορά η DigitalTime.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 12 Εξήγηση (συνέχεια) - Μπορείτε να χρησιμοποιήσετε οποιαδήποτε λέξη (εκτός των δεσμευμένων) στη θέση του DTIME_H. - Συνήθως όμως χρησιμοποιούμε το όνομα του header file με κεφαλαία και το extension μετά από underscore. Ακολουθείστε αυτή τη σύμβαση ώστε ο κώδικάς σας να είναι ευανάγνωστος.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 13 Χώροι ονομάτων - Ένας χώρος ονομάτων (namespace) είναι μια συλλογή ορισμών ονομάτων και δηλώσεων κλάσεων και συναρτήσεων. - Σκοπός είναι να διαχωρίζονται δηλώσεις που φέρουν το ίδιο όνομα, αλλά διαφορετική σημασία. - Όταν συμπεριλαμβάνουμε στον κώδικά μας τις πιο συνηθισμένες προκαθορισμένες βιβλιοθήκες, πχ. iostream, string κλπ. αυτό που συμβαίνει είναι ότι όλοι οι ορισμοί ονομάτων που γίνονται στη βιβλιοθήκη τοποθετούνται στο χώρο ονομάτων std.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 14 Χώροι ονομάτων - Το πρόγραμμά μας δε γνωρίζει για το χώρο ονομάτων std εκτός και αν καθορίσουμε ρητώς ότι θέλουμε να συμπεριλάβουμε όλους τους ορισμούς που περιλαμβάνονται εκεί. - Ο τρόπος για να γίνει αυτό είναι με την οδηγία using: using namespace std; - Όλοι οι ορισμοί ονομάτων στον κώδικά σας βρίσκονται στον καθολικό χώρο ονομάτων (global namespace) εκτός και αν ορίσετε εσείς κάποιον άλλο συγκεκριμένο χώρο.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 15 Χώροι ονομάτων - Μπορείτε να χρησιμοποιείται πολλά namespaces ταυτόχρονα αρκεί να μη συγκρούονται οι ορισμοί ονομάτων που περιέχονται σε αυτά. - Σε περίπτωση σύγκρουσης μπορείτε να χρησιμοποιήσετε τα namespaces που συγκρούονται αρκεί να μην το κάνετε ταυτόχρονα. Σε αντίθετη περίπτωση θα υπάρξει σφάλμα. ΠΑΡΑΔΕΙΓΜΑ Έστω ότι έχω τη void myFunc() να ορίζεται με διαφορετικό τρόπο στα NS1 και NS2 namespaces.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 16 Παράδειγμα (Συνέχεια) Ο ακόλουθος κώδικας είναι έγκυρος: { using namespace NS1; myFunc(); } { using namespace NS2; myFunc(); } - H πρώτη κλήση θα χρησιμοποιήσει τον ορισμό στο NS1 και η δεύτερη στο NS2. - H εμβέλεια της using είναι μέχρι το επόμενο τέλος block εντολών. Αν η using μπει στην αρχή αρχείου ισχύει για όλο το αρχείο.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 17 Δημιουργία namespace Ομαδοποιούμε ένα σετ δηλώσεων/ορισμών σε ένα namespace με το ακόλουθο: namespace Some_Name{ Some_Code } Oτιδήποτε βρίσκεται στο Some_Code θα οριστεί στο χώρο ονομάτων Some _Name Μπορούμε να έχουμε όσες ομαδοποιήσεις θέλουμε για ένα χώρο ονομάτων (στο παράδειγμα demonamespace.cpp έχουμε 2 για το Space1 και 2 για το Space2.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 18 Δηλώσεις using - Μπορούμε να επιλέξουμε να συμπεριλάβουμε ορισμένα ονόματα από ένα namespace και όχι το σύνολο των ορισμών που υπάρχουν σε αυτό. - Ο τρόπος είναι χρησιμοποιώντας τη δήλωση using. Για παράδειγμα: using std::cout; using std::cin; Έχοντας συμπεριλάβει με αυτόν τον τρόπο ότι ονόματα χρησιμοποιούμε από το std, παραλείπουμε τη δήλωση using namespace std;
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 19 Τροποποίηση Ονομάτων - Έστω ότι σκοπεύουμε να χρησιμοποιήσουμε τη myFunc() που ορίζεται στο NS1. - Μπορώ να αναφερθώ σε αυτή (και σε οποιοδήποτε άλλο αντικείμενο) χρησιμοποιώντας το χώρο ονομάτων και τον τελεστή επίλυσης εμβέλειας ως εξής: NS1::myFunc() - Μπορούμε να αναφερθούμε σε ένα όνομα κατά τον παραπάνω τρόπο ακόμα και όταν είμαστε στην εμβέλεια κάποιου άλλου namespace όπου ορίζεται το ίδιο όνομα, πχ. using namespace NS2 //in NS2 myFunc() is also defined NS1::myFunc() - Χρησιμοποιείται κυρίως όταν καθορίζουμε τον τύπο παραμέτρου μιας συνάρτησης (γιατί?), πχ., int getInput(std::istream inputStream);
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 20 Ορισμός κλάσης σε χώρο ονομάτων - Μπορούμε να βάλουμε τον ορισμό της κλάσης DigitalTime σε ένα ξεχωριστό χώρο ονομάτων έστω DTimeLuke (βλέπε παράδειγμα). - Ο χώρος ονομάτων DTimeLuke συνδέει τα αρχεία dtime.h και dtime.cpp. Γενικότερα ένας χώρος ονομάτων μπορεί να συνδέει πολλά αρχεία. - Για να χρησιμοποιήσουμε τώρα την DigitalTime στο αρχείο εφαρμογής πρέπει να τη συμπεριλάβουμε με κάποιον τρόπο: using namespace DTimeLuke; using DTimeLuke::DigitalTime; - Προσοχή: Όταν δίνεται όνομα σε δικό σας namespace φροντίστε να βάζετε λέξεις που είναι απίθανο να χρησιμοποιηθούν από άλλους προγραμματιστές που συνεργάζεστε.
Αντικειμενοστραφής Ξεχωριστή Μεταγλώττιση & Χώροι Ονομάτων/ Slide 21 Ανώνυμοι Χώροι Ονομάτων - Κάθε μονάδα μεταγλώττισης (αρχείο) έχει και ένα ξεχωριστό χώρο ονομάτων (ανώνυμος χώρος). - Η ομαδοποίηση για τον ανώνυμο χώρο γράφετε σαν: namespace{ void sampleFunc(); } - Όλα τα ονόματα που ορίζονται στον ανώνυμο χώρο είναι τοπικά στη μονάδα μεταγλώττισης. - Οι ανώνυμοι χώροι ονομάτων αντικαθιστούν το προσδιοριστικό static - Χρήσιμοι για απόκρυψη βοηθητικών συναρτήσεων