Μήτρες (templates) Μία μήτρα είναι ένα κομμάτι κώδικα που περιέχει παραμέτρους οι οποίες δέχονται ως τιμές τύπους δεδομένων. Είναι ένας μηχανισμός για τη δημιουργία προγραμμάτων ανεξάρτητων από τον τύπο των δεδομένων που θα επεξεργαστούν. Π.χ. Αν T είναι παραμετρικός τύπος η δήλωση Τ x,y,z; σε ένα κομμάτι κώδικα, θα ορίσει τον τύπο των μεταβλη- τών x,y και z όταν ως τιμή του T δοθεί ένας τύπος δεδομέ- νου.
Ως παραμετρικοί τύποι μπορούν να δοθούν, εκτός από τους βασικούς τύπους δεδομένων που ορίζει η C++ και τύποι δεδομένων που ορίζονται από τον χρήστη, όπως τύποι κλάσεων, δομών, ενώσεων κλπ. Η C++ επιτρέπει στις συναρτήσεις, στις κλάσεις καθώς και σε άλλες δομές να περιέχουν παραμετρικούς τύπους. Έτσι ο κώδικας γίνεται ανεξάρτητος από το είδος των δεδομένων που θα τον χρησιμοποιήσουν. Μία μήτρα δεν υλοποιεί εκτελέσιμο κώδικα, ο κώδικας υλοποιείται όταν δοθεί στις παραμέτρους ως τιμή ένας συγκεκριμένος τύπος δεδομένου.
Μήτρες συναρτήσεων Ως μήτρες συναρτήσεων ορίζονται συναρτή- σεις οι οποίες ως ορίσματα δέχονται παρα- μέτρους. Ο εκτελέσιμος κώδικας της συνάρτησης υλοποιείται όταν στη θέση της παραμέτρου δοθεί συγκεκριμένος τύπος δεδομένου. Δήλωση μήτρας συνάρτησης template Επιστρεφόμενη_τιμή όνομα(ορίσματα)
Π.χ template T power(T a,int exp) { T ans = a; while(--exp>0) ans=ans*a; return ans; }
Στον ορισμό μιας μήτρας συνάρτησης έχουμε: Την κεφαλίδα template όπου: Η λέξη κλειδί template δηλώνει ότι η συνάρτηση που ακολουθεί δέχεται παραμετρικούς τύπους δεδομένων. Η λέξη class δεν σημαίνει ότι οι παράμετροι είναι στον τύπο κάποιας κλάσης απλά σημαίνει ότι είναι παραμετρικοί τύποι δεδομένων.
Το πρότυπο της συνάρτησης όπου: Στις τυπικές παραμέτρους που δέχεται ως ορίσματα η συνάρτηση πρέπει να αναγράφεται τουλάχιστον μια φορά κάθε παραμετρικός τύπος που ορίζεται στην κεφαλίδα της μήτρας. Η εμφάνιση του παραμετρικού τύπου ως επιστρεφόμενης τιμής δεν αντικαθιστά την ανάγκή εμφάνισης της αντίστοιχης παραμέτρου ως όρισμα.
Το σώμα της συνάρτησης όπου: Πρέπει να εμφανίζεται τουλάχιστον μια φόρά κάθε παράμετρος που δηλώθηκε στην κεφαλίδα.
Κατά τη μεταγλώττιση του κώδικα, στον οποίο περιλαμβάνονται μήτρες, όταν ο μεταγλωττιστής φτάσει στο σημείο όπου καλείται μια συνάρτηση, ελέγχει πρώτα αν υπάρχει πρότυπο που να ταυτί- ζεται με την διαδικασία κλήσης της. Αν όχι ελέγχει αν υπάρχει μήτρα στην οποία, αν αντικατασταθούν οι παράμετροι με τους τύπους που ορίζονται από την κλήση της συνάρτησης θα προκύψει πρότυπο συμβατό με την συγκεκριμένη κλήση. Αν ναι δημιουργεί και ενσωματώνει στην αντίστοιχη θέση εκτελέσιμο κώδικα στον οποίο έχει αντικαταστήσει τους παραμετρικούς τύπους με τους τύπους δεδομένων που απαιτεί η κλήση της συνάρτησης.
Κάθε στιγμιότυπο της μήτρας δημιουργεί και μια καινούργια έκδοση εκτελέσιμου κώδικα για τη συνάρτηση. Η δημιουργία νέας έκδοσης εκτελέσιμου κώδικα γίνεται μόνον αν δεν έχει δημιουρ- γηθεί προηγουμένως στιγμιότυπο της μήτρας με τον ίδιο τύπο παραμέτρων.
Παράδειγμα #include template T power(T a,int exp) { T ans = a; while(--exp>0) ans=ans*a; return ans; } void main() { int k=2,m; m=power(k,3); //Δημιουργείται κώδικας για το πρότυπο int power(int,int) cout << m <<"\n"; m=power(k,4); // Χρησιμοποιείται ο κώδικας που δημιουργήθηκε για το // πρότυπο int power(int,int) cout << m <<"\n"; float r=2.5,x; x=power(r,3); //Δημιουργείται κώδικας για το πρότυπο int power(float,int) cout << x <<"\n"; }
Είναι προφανές ότι οι τελεστές, οι συναρτήσεις ή οι τυχόν άλλες πράξεις οι οποίες χρησιμοποιούνται στο εσωτερικό μιας συνάρτησης, η οποία ορίζεται ως μήτρα, θα πρέπει να είναι στοιχεία συμβατά με τους τύπους δεδομένων που θα δοθούν στη θέση των παραμετρικών τύπων που ορίζονται σ’ αυτήν. Σε διαφορετική περίπτωση θα πρέπει να ορισθούν οι κατάλληλες επικαλύψεις ώστε να μπορεί να λειτουργήσει η συνάρτηση.
Ταυτοποίηση τυπικών παραμέτρων σε μήτρες συναρτήσεων Αν σε μία μήτρα συνάρτησης δοθεί διαφορετικός τύπος ορισμάτων για τα ορίσματα που δεν ορίζονται παραμετρικά ο μεταγλωττιστής αποτυγχάνει να δημιουργήσει στιγμιότυπο της μήτρας. Για να αποφύγουμε το σχετικό σφάλμα σύνδεσης (Linking error) πρέπει να δηλωθεί στο πρόγραμμα ένα πρότυπο συνάρτησης που να ταυτίζεται με το πρότυπο της μήτρας στο οποίο οι παραμετρικοί τύποι ορισμάτων θα έχουν αντικατασταθεί με κάποιον συμβατό με τη μήτρα τύπο δεδομένων.
Π.χ. #include float power(float a,unsigned int exp); //Πρότυπο για τη μετατροπή του τύπου template T power(T a,unsigned int exp) { int i; T r=a; //Μήτρα for(i=1; i<exp; i++) r*=a; return r; } void main() { float t=3.5; int k=2; cout <<power(k,t)<<'\n'; // Ο t μετατρέπεται σε unsigned int με τιμή 3 }
Ο μεταγλωττιστής, σε κάθε στιγμιότυπο της μήτρας, ελέγχει το πρότυπο και μετατρέπει τους μη παραμετρικούς τύπους στον τύπο που δηλώνεται στο πρότυπο. Στη θέση των παραμέτρων τοποθετεί τον τύπο δεδομένων που δίνεται στην αντίστοιχη θέση με την κλήση της συνάρτησης.
Παραμετροποίηση τύπων Εκτός από συναρτήσεις μπορούμε να παραμετροποιήσουμε και τύπους δεδομένών που ορίζονται από τον χρήστη. Έτσι μπορούμε να ορίσουμε κλάσεις, δομές ή άλλους τύπους σύνθετων δεδομένων οι οποίοι να ορίζουν εσωτερικά δεδομένα τα οποία θα δηλώνονται σε αυτούς παραμετρικά.
Δήλωση μήτρας για παραμετροποίηση κλάσεων template class name{. T data_name;. };
Η δήλωση μήτρας για την παραμετροποίηση μιας κλάσεις περιέχει : Την κεφαλίδα template η οποία περιλαμβάνει: Δηλώσεις παραμετρικών τύπων οι οποίες εισάγονται με τη λέξη κλειδί class. Δηλώσεις τύπου για σταθερές οι τιμές των οποίων θα δοθούν κατά τη δημιουργία των στιγμιότυπων της κλάσης.
Τη δήλωση της κλάσης στην οποία μπορούν να υπάρχουν δηλώσεις μεταβλητών στον τύπο των παραμέτρων που ορίζονται στην κεφαλίδα και να γίνεται χρήση των σταθερών που δηλώνονται σε αυτήν.
Π.χ. #include template class A{ TYPE a[SIZE]; public: A(); void out(); }; template A ::A() { int i; for(i=0; i<SIZE; i++) cin >>a[i]; } template void A ::out() { int i; for(i=0; i<SIZE; i++) cout << a[i] <<" "; cout <<"\n"; } void main() { A a; A b; a.out(); b.out(); }
Εφόσον μια κλάση δηλώνεται ως μήτρα οι συναρτήσεις μέλη που ανήκουν σ’ αυτήν πρέπει να δηλωθούν επίσης με τη μορφή μήτρας. Η δήλωση μιας τέτοιας συνάρτησης απαιτεί εκτός από το όνομα της κλάσης στην οποία ανήκει, να αναφέρονται και τα ονόματα των παραμέτρων τις οποίες δέχεται η κλάση. Δεν απαιτείται να υπάρχει αναφορά των παραμέτρων στα ορίσματα και το σώμα της συνάρτησης όπως θα συνέβαινε αν η συνάρτηση δηλωνόταν ως μήτρα αυτόνομα.
Η δήλωση μιας συνάρτησης μέλος σε μια μήτρα κλάσης πρέπει να περιέχει εκτός από το όνομα της κλάσης, πριν από τον τελεστή εμβέλειας :: μέσα σε <> και τα ονόματα των παραμέτρων τις οποίες χρησιμοποιεί η μήτρα. Ακόμη πριν από τη δήλωση πρέπει να προηγείται η κεφαλίδα template η οποία χρησιμοποιείται για τη δήλωση της κλάσης ως μήτρα.
Δήλωση συνάρτησης μέλους μήτρας template Τύπος κλάση ::όνομα_συνάρτησης(….) {…} Π.χ template void A ::out() {.. }
Κατά τη δημιουργία αντικειμένων στον τύπο μιας κλάσης που προέρχεται από μήτρα πρέπει εντός των <> να δηλωθεί ο τύπος των δεδομένων για τους παραμετροποιημένους τύπους και οι τιμές των σταθερών που δηλώνονται στην κεφαλίδα template. Π.χ. A a;
Για κάθε στιγμιότυπο στον τύπο μιας μήτρας ο μεταγλωττιστής δημιουργεί εκτελέσιμο κώδικα για μια καινούργια κλάση αντικαθιστώντας τις παραμέτρους και τις τιμές των σταθερών με αυτές που δηλώνονται στην αντίστοιχη θέση στο πρόγραμμα. Νέος εκτελέσιμος κώδικας θα δημιουργηθεί ακόμη και αν δύο στιγμιότυπα έχουν τον ίδιο τύπο παραμέτρων αλλά διαφέρουν στις τιμές των σταθερών που δηλώνονται παραμετρικά. Αν για κάποια αντικείμενα δηλωθεί ο ίδιος τύπος παραμέτρων και οι ίδιες τιμές για τις σταθερές τότε δημιουργείται μόνο μια έκδοση της κλάσης για αυτά τα αντικείμενα.
Π.χ. Οι δηλώσεις A a; A b; A c; Θα δημιουργήσουν κοινό κώδικα για τα αντικείμενα a και c ενώ ξεχωριστός κώδικας θα δημιουργηθεί για το αντικείμενο b.