User Tools

Site Tools


cpp:class_templates_specialization

Εξειδίκευση ενός υφιστάμενου template

Εξειδίκευση της κλάσης Box στην περίπτωση που η παράμετρος είναι δείκτης

Συχνά είναι απαραίτητο να προδιαγράψουμε διαφορετική λειτουργικότητα σε ένα template με βάση τον τύπο των δεδομένων που θα εισαχθεί τελικά στην κλάση. Για παράδειγμα, για την κλάση Box<T> που είδαμε προηγουμένως, οφείλουμε να εισάγουμε διαφορετική λειτουργικότητα όταν ο τύπος δεδομένων είναι δείκτης αντί για κανονική μεταβλητή. Ο λόγος είναι ότι σε αυτή την περίπτωση δεν αρκεί η απλή αντιγραφεί του δείκτη, αλλά θα πρέπει να δεσμευθεί ο απαραίτητος χώρος στη μνήμη προκειμένου η κλάση να δημιουργήσει ένα αντίγραφο της υφιστάμενης πληροφορίας που περνιέται μέσω του δείκτη. Το παράδειγμα εξειδίκευσης της κλάσης Box<T> όταν περνιέται δείκτης δίνεται παρακάτω:

BoxPtr.hpp
#ifndef _BOXTPTR_HPP_
#define _BOXTPTR_HPP_
 
#include "Box.hpp"
 
template<typename T>
class Box<T*> {
  T *e;
public:
  Box();
  Box(T* e);
  Box(const Box<T*>& b);
  ~Box();
  T* get() const;
  void set(T* e);
  template <T*>
  friend std::ostream& operator<<(std::ostream& out, const Box<T*>& t);
  Box<T*>& operator=(Box<T*>& b);
};
 
template <typename T>
Box<T*>::Box() {
  this->e = nullptr;
}
 
template <typename T>
Box<T*>::Box(T* e) {
  this->e = new T;
  *(this->e) = *e;
}
 
template <typename T>
Box<T*>::Box(const Box<T*>& b) {
  this->e = new T;
  *(this->e) = *b.e;
}
 
template <typename T>
Box<T*>::~Box() {
  delete this->e;
}
 
template <typename T>
T* Box<T*>::get() const { 
  T *ce = new T;
  *ce = *e;
  return ce;
}
 
template <typename T>
void Box<T*>::set(T *e) { 
  if(this->e != nullptr)
    delete this->e;
  this->e = new T;
  *(this->e) = *e;
}
 
template <typename T>
std::ostream& operator<<(std::ostream& out, const Box<T*>& t) {
  T* ptr = t.get();
  out << *ptr;
  delete ptr;
  return out;
}
 
template <typename T>
Box<T*>& Box<T*>::operator=(Box<T*>& b){
  set(b.e);
  return *this;
}
#endif

Παρακάτω παρατίθεται κώδικας ο οποίος χρησιμοποιεί την παραπάνω εξειδίκευση του template.

BoxPtrUsage.cpp
#include <iostream>
#include "BoxPtr.hpp"
#include "Student.hpp"
 
using namespace std;
 
int main() {
  int a=5;
  double d(4.23);
  Student kate = {"Kate", 1234};
 
  Box<int*> intBox(&a);
  Box<double*> doubleBox(&d);
  Box<Student*> 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. Σε αυτή την περίπτωση δεν αρκεί να δεσμευθεί η μνήμη για ένα χαρακτήρα, αλλά θα πρέπει α) να αναγνωρίσουμε το μέγεθος της συμβολοσειράς που πρέπει να αντιγραφεί και β)πριν γίνει η αντιγραφή της συμβολοσειράς να δεσμευθεί ο απαραίτητος χώρος για αυτή.

BoxCharPtr.hpp
#ifndef _BOXCHARPTR_HPP_
#define _BOXCHARPTR_HPP_
 
#include "Box.hpp"
#include <cstring>
 
template <>
class Box<char *> {
  char *e;
public:
  Box();
  Box(char *e);
  ~Box();
  Box(const Box<char*>& b);
  char* get() const;
  void set(char *e);
  Box<char*>& operator=(Box<char*>& b);
  friend std::ostream& operator<<(std::ostream& out, const Box<char*>& t);
};
 
Box<char*>::Box() {
  this->e = nullptr;
}
 
Box<char*>::Box(char *e) {
  this->e = new char[strlen(e)+1];
  strcpy(this->e, e);
}
 
Box<char*>::Box(const Box<char*>& b) {
  this->e = new char[strlen(b.e)+1];
  strcpy(this->e, b.e);
}
 
Box<char*>::~Box() {
  delete [] this->e;
}
 
char* Box<char*>::get() const { 
  char *ce = new char[strlen(e)+1];
  strcpy(ce, e);
  return ce;
}
 
void Box<char *>::set(char *e) {
  if(this->e!=nullptr)
    delete [] this->e;
  this->e = new char[strlen(e)+1];
  strcpy(this->e, e);
}
 
Box<char*>& Box<char*>::operator=(Box<char*>& b){
  set(b.e);
  return *this;
}
 
std::ostream& operator<<(std::ostream& out, const Box<char*>& t) {
  out << t.e;
  return out;
}
#endif

Παράδειγμα κώδικα που χρησιμοποιεί την παραπάνω εξειδικευμένη κλάση Box<char*> δίνεται παρακάτω:

BoxCharPtrUsage.cpp
#include <iostream>
 
#include "BoxCharPtr.hpp"
 
using namespace std;
 
int main() {
  char greeting[64] = "Wellcome C++ in CE325 course";
 
  Box<char*> greetingBox(greeting);
 
  char* greeting_copy = greetingBox.get();
  cout << greeting_copy << endl;
  delete greeting_copy;
 
  Box<char*> msgBox = greetingBox;
  char* msg = msgBox.get();
  cout << msg << endl;
  delete msg; 
}

Στον παραπάνω κώδικα αντικαταστήστε την εντολή #include “BoxCharPtr.hpp” με την εντολή #include “BoxPtr.hpp” και στη συνέχεια με την εντολή #include “Box.hpp”. Παρατηρήστε την αλλαγή στη συμπεριφορά του προγράμματος. Σε τι οφείλεται αυτό;

cpp/class_templates_specialization.txt · Last modified: 2022/05/26 10:47 by gthanos