====== Εξειδίκευση ενός υφιστάμενου template ====== ===== Εξειδίκευση της κλάσης Box στην περίπτωση που η παράμετρος είναι δείκτης ===== Συχνά είναι απαραίτητο να προδιαγράψουμε διαφορετική λειτουργικότητα σε ένα template με βάση τον τύπο των δεδομένων που θα εισαχθεί τελικά στην κλάση. Για παράδειγμα, για την κλάση **Box** που είδαμε προηγουμένως, οφείλουμε να εισάγουμε διαφορετική λειτουργικότητα όταν ο τύπος δεδομένων είναι δείκτης αντί για κανονική μεταβλητή. Ο λόγος είναι ότι σε αυτή την περίπτωση δεν αρκεί η απλή αντιγραφεί του δείκτη, αλλά θα πρέπει να δεσμευθεί ο απαραίτητος χώρος στη μνήμη προκειμένου η κλάση να δημιουργήσει ένα αντίγραφο της υφιστάμενης πληροφορίας που περνιέται μέσω του δείκτη. Το παράδειγμα εξειδίκευσης της κλάσης **Box** όταν περνιέται δείκτης δίνεται παρακάτω: #ifndef _BOXTPTR_HPP_ #define _BOXTPTR_HPP_ #include "Box.hpp" template class Box { T *e; public: Box(); Box(T* e); Box(const Box& b); ~Box(); T* get() const; void set(T* e); template friend std::ostream& operator<<(std::ostream& out, const Box& t); Box& operator=(Box& b); }; template Box::Box() { this->e = nullptr; } template Box::Box(T* e) { this->e = new T; *(this->e) = *e; } template Box::Box(const Box& b) { this->e = new T; *(this->e) = *b.e; } template Box::~Box() { delete this->e; } template T* Box::get() const { T *ce = new T; *ce = *e; return ce; } template void Box::set(T *e) { if(this->e != nullptr) delete this->e; this->e = new T; *(this->e) = *e; } template std::ostream& operator<<(std::ostream& out, const Box& t) { T* ptr = t.get(); out << *ptr; delete ptr; return out; } template Box& Box::operator=(Box& b){ set(b.e); return *this; } #endif Παρακάτω παρατίθεται κώδικας ο οποίος χρησιμοποιεί την παραπάνω εξειδίκευση του //template//. #include #include "BoxPtr.hpp" #include "Student.hpp" using namespace std; int main() { int a=5; double d(4.23); Student kate = {"Kate", 1234}; Box intBox(&a); Box doubleBox(&d); Box studentBox(&kate); Student* kate_ptr = studentBox.get(); cout << *kate_ptr << endl; delete kate_ptr; kate.setName("Katerina"); cout << kate << endl; } Στον παραπάνω κώδικα αντικαταστήστε την εντολή ''#include "BoxPtr.hpp"'' με την εντολή ''#include "Box.hpp"'' και παρατηρήστε την αλλαγή στη συμπεριφορά του προγράμματος. To πρόγραμμα θα τερματίσει τη λειτουργία του (Γιατί?) με ένα μήνυμα της μορφής: *** Error in `./BoxPtrUsage': double free or corruption (out): 0x00007ffc4f49ed10 *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f40da1757e5] /lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7f40da17e37a] /lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f40da18253c] ===== Εξειδίκευση της κλάσης Box στην περίπτωση που η παράμετρος είναι δείκτης σε χαρακτήρα ===== Το παραπάνω παράδειγμα είναι ικανοποιητικό εάν περαστεί ως δείκτης ένα αντικείμενο τύπου **int* ** ή **Student* **, όμως δεν είναι ικανοποιητικό εάν περαστεί ένας δείκτης τύπου **char* ** που αντιπροσωπεύει ένα C string. Σε αυτή την περίπτωση δεν αρκεί να δεσμευθεί η μνήμη για ένα χαρακτήρα, αλλά θα πρέπει α) να αναγνωρίσουμε το μέγεθος της συμβολοσειράς που πρέπει να αντιγραφεί και β)πριν γίνει η αντιγραφή της συμβολοσειράς να δεσμευθεί ο απαραίτητος χώρος για αυτή. #ifndef _BOXCHARPTR_HPP_ #define _BOXCHARPTR_HPP_ #include "Box.hpp" #include template <> class Box { char *e; public: Box(); Box(char *e); ~Box(); Box(const Box& b); char* get() const; void set(char *e); Box& operator=(Box& b); friend std::ostream& operator<<(std::ostream& out, const Box& t); }; Box::Box() { this->e = nullptr; } Box::Box(char *e) { this->e = new char[strlen(e)+1]; strcpy(this->e, e); } Box::Box(const Box& b) { this->e = new char[strlen(b.e)+1]; strcpy(this->e, b.e); } Box::~Box() { delete [] this->e; } char* Box::get() const { char *ce = new char[strlen(e)+1]; strcpy(ce, e); return ce; } void Box::set(char *e) { if(this->e!=nullptr) delete [] this->e; this->e = new char[strlen(e)+1]; strcpy(this->e, e); } Box& Box::operator=(Box& b){ set(b.e); return *this; } std::ostream& operator<<(std::ostream& out, const Box& t) { out << t.e; return out; } #endif Παράδειγμα κώδικα που χρησιμοποιεί την παραπάνω εξειδικευμένη κλάση **Box** δίνεται παρακάτω: #include #include "BoxCharPtr.hpp" using namespace std; int main() { char greeting[64] = "Wellcome C++ in CE325 course"; Box greetingBox(greeting); char* greeting_copy = greetingBox.get(); cout << greeting_copy << endl; delete greeting_copy; Box msgBox = greetingBox; char* msg = msgBox.get(); cout << msg << endl; delete msg; } Στον παραπάνω κώδικα αντικαταστήστε την εντολή ''#include "BoxCharPtr.hpp"'' με την εντολή ''#include "BoxPtr.hpp"'' και στη συνέχεια με την εντολή ''#include "Box.hpp"''. Παρατηρήστε την αλλαγή στη συμπεριφορά του προγράμματος. Σε τι οφείλεται αυτό;