User Tools

Site Tools


cpp:constructors_destructors

This is an old revision of the document!


Κατασκευαστές και καταστροφείς της κλάσης

Στο προηγούμενο παράδειγμα της κλάσης Rectangle δεν δηλώθηκε κάποιος κατασκευαστής. Ο κατασκευαστής της κλάσης είναι μέθοδος που έχει το όνομα της κλάσης και δεν έχει επιστρεφόμενο τύπο. Ο ρόλος του κατασκευαστή της κλάσης είναι ο εξής: α) δεσμεύει τον απαραίτητο χώρο στη μνήμη για την ύπαρξη του αντικειμένου και β) πιθανόν (όχι πάντα) αρχικοποιεί τις τιμές των πεδίων της κλάσης. Για την κλάση Rectangle παρακάτω δηλώνεται ο κατασκευαστής Rectangle(int w, int h).

Rectangle.hpp
#include <iostream>
using namespace std;
 
class Rectangle {
  private:
    int width, height;
  public:
    Rectangle(int w, int h);
    void setWidth(int w);
    void setHeight(int h);
    int getWidth() const;
    int getHeight() const;
};
 
Rectangle::Rectangle(int w, int h) {
  width = w; height = h;
}
 
void Rectangle::setWidth(int w) { width = w; }
void Rectangle::setHeight(int h) { height = h; }
int Rectangle::getWidth() const { return width; }
int Rectangle::getHeight() const { return height; }
RectangleUsage.cpp
#include "Rectangle.cpp"
 
int main () {
  Rectangle rect(5,6);
  cout << "area: " << rect.getWidth() * rect.getHeight() << endl;
  return 0;
}

Σε αναλογία με τις μεθόδους ο κατασκευαστής μπορεί να οριστεί εντός της κλάσης, όπως παρακάτω. Η δήλωση εντός της κλάσης συνεπάγεται τη δήλωση από τον προγραμματιστή του κατασκευαστή ως inline.

Rectangle.cpp
class Rectangle {
  private:
    int width, height;
  public:
    Rectangle(int w, int h){
      width = w; height = h;
    }
};

Default κατασκευαστής

Εάν δεν οριστεί κανένας κατασκευαστής σε μία κλάση τότε για την κλάση ορίζεται από τον μεταγλωττιστή ο default κατασκευαστής. Ο κατασκευαστής αυτός δεν έχει ορίσματα. Τα πεδία της κλάσης αρχικοποιούνται ως εξής:

  1. για πεδία που έχουν βασικού τύπους (int, double, char κλπ) οι τιμές είναι τυχαίες.
  2. για πεδία αναφορικού τύπου καλείται ο default κατασκευαστής της εκάστοτε κλάσης.

Εάν έχετε δηλώσει τουλάχιστον ένα κατασκευαστή που δεν είναι ο default κατασκευαστής χωρίς παραμέτρους τότε ο μεταγλωττιστής δεν κατασκευάζει αυτόματα τον default κατασκευαστή. Σε περίπτωση που ο default κατασκευαστής απαιτείται λαμβάνετε μήνυμα λάθους από τον μεταγλωττιστή.

Υπερφόρτωση κατασκευαστών

Σε αναλογία με την υπερφόρτωση συναρτήσεων η C++ υποστηρίζει και υπερφόρτωση κατασκευαστών. Συγκεκριμένα, μπορείτε να έχετε περισσότερους από έναν κατασκευαστές αρκεί αυτοί να έχουν διαφορετικό αριθμό ή τύπο ορισμάτων. Δείτε το παρακάτω παράδειγμα της κλάση Rectangle, όπου δίνονται δύο επιπλέον κατασκευαστές, ένα κατασκευαστή που λαμβάνει ένα κοινό όρισμα και για τις δύο διαστάσεις του παραλληλογράμμου και ένα κατασκευαστή χωρίς ορίσματα που αναθέτει τυχαίες τιμές στις διαστάσεις του παραλληλογράμμου.

Rectangle.hpp
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
 
class Rectangle {
  private:
    int width, height;
  public:
    Rectangle(int w, int h);
    Rectangle(int s);
    Rectangle();
    void setWidth(int w);
    void setHeight(int h);
    int getWidth() const;
    int getHeight() const;
};
 
Rectangle::Rectangle(int w, int h) {
  width = w; height = h;
}
 
Rectangle::Rectangle(int s) {
  width = s; height = s;
}
 
Rectangle::Rectangle() {
  srand(time(NULL));
  width = rand() % 10 + 1; height = rand() % 10 + 1;
}
 
void Rectangle::setWidth(int w) { width = w; }
void Rectangle::setHeight(int h) { height = h; }
int Rectangle::getWidth() const { return width; }
int Rectangle::getHeight() const { return height; }
RectangleUsage.cpp
#include "Rectangle.hpp"
 
int main () {
  Rectangle rect(5,6);
  cout << "area: " << rect.getWidth() * rect.getHeight() << endl;
  return 0;
}

Χρήση member initialization list για την αρχικοποίηση των μελών της κλάσης

Παραπάνω συναντήσαμε τον κατασκευαστή με δύο ορίσματα

Rectangle::Rectangle(int w, int h) {
  width = w; height = h;
}

Αντί για την παραπάνω δήλωση θα μπορούσατε να γράψετε ισοδύναμα:

Rectangle::Rectangle(int w, int h) : width(w) {
  height = h;
}

ή

Rectangle::Rectangle(int w, int h) : width(w), height(h) {}

Ο παραπάνω τρόπος αρχικοποίησης μπορεί να επεκταθεί και να χρησιμοποιηθεί για την κλήση κατασκευαστών των πεδίων της κλάσης, όταν αυτά είναι αναφορικού τύπου. Για παράδειγμα, δείτε παρακάτω την κλάση Cuboid η οποία διαθέτει ένα πεδίο της κλάσης Rectangle.

Cuboid.cpp
#include <iostream>
using namespace std;
 
#include "Rectangle.cpp"
 
class Cuboid {
  private:
    int length;
    Rectangle rect;
  public:
    Cuboid(Rectangle r, int l);
    Cuboid(int w, int h, int l);
    void setRectangle(Rectangle r) {rect = r;}
    Rectangle getRectangle() {return rect;}
    void setLength(int l) { length = l; }
    int getLength() const { return length; }
    int volume();
};
 
Cuboid::Cuboid(Rectangle r, int l) : rect(r), length(l) { }
Cuboid::Cuboid(int w, int h, int l) : rect(w,h), length(l) { }
 
int Cuboid::volume() {
  return length * rect.getWidth() * rect.getHeight();
}
CuboidUsage.cpp
#include "Cuboid.cpp"
 
int main () {
  Rectangle rect(5,6);
  Cuboid cuboid1(rect, 10);
  cout << "volume: " << cuboid1.volume() << endl;
  Cuboid cuboid2(2,3,4);
  cout << "volume: " << cuboid2.volume() << endl;
  return 0;
}

Παρατηρήστε πως ορίζονται οι κατασκευαστές της κλάσης Cuboid με χρήση member initialization list.

Cuboid::Cuboid(Rectangle r, int l) : rect(r), length(l) { }
Cuboid::Cuboid(int w, int h, int l) : rect(w,h), length(l) { }

Στην 1η περίπτωση γίνεται ανάθεση του αντικειμένου r στο πεδίο rect. Στην 2η περίπτωση καλείται ο κατασκευαστής της κλάσης Rectangle με ορίσματα (w,h).

Καταστροφέας της κλάσης

Σε αναλογία με τον κατασκευαστή της κλάσης η C++ δηλώνει και τον καταστροφέα της κλάσης. Ο καταστροφέας της κλάσης επιτρέπει να γίνουν οι απαραίτητες εργασίες καθαρισμού για το αντικείμενο που καταστρέφεται. Για παράδειγμα είναι πιθανόν κατά την δημιουργία του αντικειμένου να έχει δεσμευθεί μνήμη την οποία πρέπει να ελευθερώσουμε ή να έχουν ανοιχθεί αρχεία τα οποία κατά την καταστροφή του αντικειμένου πρέπει να τα κλείσουμε.

Στο παρακάτω παράδειγμα βλέπετε την κλάση Rectangle όπου τα πεδία width και height είναι δείκτες. Σε αυτή την περίπτωση θα πρέπει να δεσμεύουμε την απαραίτητη μνήμη με χρήση του τελεστή new. Στον καταστροφέα την κλάσης απελευθερώνουμε τη μνήμη που δεσμεύσαμε προηγούμενα.

Rectangle.cpp
#include <iostream>
#include <cstdlib>
using namespace std;
 
class Rectangle {
  private:
    int *width, *height;
  public:
    Rectangle(int w, int h);
    ~Rectangle();
    void setWidth(int w);
    void setHeight(int h);
    int getWidth();
    int getHeight();
};
 
Rectangle::Rectangle(int w, int h) {
  width = new (nothrow) int;    
  height = new (nothrow) int;
  if(width == NULL || height == NULL) {
    cerr << "Memory allocation failure!\n";
    exit(-1);
  }
  *width = w; *height = h;
  cout << "Constructing rectangle (w:"<< *width <<", h:"<<*height<<")\n";
}
 
Rectangle::~Rectangle() {
  cout << "Destructing rectangle (w:"<< *width <<", h:"<<*height<<")\n";
  delete width;
  delete height;
}
 
void Rectangle::setWidth(int w) { *width = w; }
void Rectangle::setHeight(int h) { *height = h; }
int Rectangle::getWidth() { return *width; }
int Rectangle::getHeight() { return *height; }
 
int main () {
  Rectangle rect(5,6);
  cout << "area: " << rect.getWidth() * rect.getHeight() << endl;
  return 0;
}
cpp/constructors_destructors.1586332694.txt.gz · Last modified: 2020/04/08 06:58 (external edit)