User Tools

Site Tools


cpp:constructors_destructors

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
cpp:constructors_destructors [2017/04/20 08:11] gthanoscpp:constructors_destructors [Unknown date] (current) – external edit (Unknown date) 127.0.0.1
Line 1: Line 1:
-====== Κατασκευαστές και καταστροφέας της κλάσης ======+====== Κατασκευαστές και καταστροφείς της κλάσης ======
  
-Στο προηγούμενο παράδειγμα της κλάσης //Rectangle// δεν δηλώθηκε κάποιος κατασκευαστής. Ο κατασκευαστής της κλάσης είναι μέθοδος που έχει το όνομα της κλάσης και δεν έχει επιστρεφόμενο τύπο. Ο ρόλος του κατασκευαστή της κλάσης είναι ο εξής: //α)// δεσμεύει τον απαραίτητο χώρο στη μνήμη για την ύπαρξη του αντικειμένου και //β)// πιθανόν (όχι πάντα) αρχικοποιεί τις τιμές των πεδίων της κλάσης. Για την κλάση //Rectangle// παρακάτω δηλώνεται ο κατασκευαστής ''Rectangle(int w, int h)''.+Στο προηγούμενο παράδειγμα της κλάσης //Rectangle// δεν δηλώθηκε κάποιος κατασκευαστής. Ο κατασκευαστής της κλάσης είναι μέθοδος που έχει το όνομα της κλάσης και δεν έχει επιστρεφόμενο τύπο. Ο ρόλος του κατασκευαστή της κλάσης είναι να αρχικοποιήσει τις τιμές των πεδίων της κλάσης. Σημειώστε, ότι ο κατασκευαστής της κλάσης στη C++ δεν δεσμεύει χώρο στη μνήμη, όπως συμβαίνει στη Java. Για την κλάση //Rectangle//παρακάτω δηλώνεται ο κατασκευαστής ''Rectangle(int w, int h)''.
  
-<code c++ Rectangle.cpp>+<code cpp Rectangle.hpp>
 #include <iostream> #include <iostream>
 using namespace std; using namespace std;
Line 14: Line 14:
     void setWidth(int w);     void setWidth(int w);
     void setHeight(int h);     void setHeight(int h);
-    int getWidth(); +    int getWidth() const
-    int getHeight();+    int getHeight() const;
 }; };
  
Line 24: Line 24:
 void Rectangle::setWidth(int w) { width = w; } void Rectangle::setWidth(int w) { width = w; }
 void Rectangle::setHeight(int h) { height = h; } void Rectangle::setHeight(int h) { height = h; }
-int Rectangle::getWidth() { return width; } +int Rectangle::getWidth() const { return width; } 
-int Rectangle::getHeight() { return height; }+int Rectangle::getHeight() const { return height; } 
 +</code> 
 + 
 +<code cpp RectangleUsage.cpp> 
 +#include "Rectangle.hpp"
  
 int main () { int main () {
Line 34: Line 38:
 </code> </code>
  
-<WRAP center round tip 80%>+<WRAP center round info 80%>
 Σε αναλογία με τις μεθόδους ο κατασκευαστής μπορεί να οριστεί εντός της κλάσης, όπως παρακάτω. Η δήλωση εντός της κλάσης συνεπάγεται τη δήλωση από τον προγραμματιστή του κατασκευαστή ως //inline//. Σε αναλογία με τις μεθόδους ο κατασκευαστής μπορεί να οριστεί εντός της κλάσης, όπως παρακάτω. Η δήλωση εντός της κλάσης συνεπάγεται τη δήλωση από τον προγραμματιστή του κατασκευαστή ως //inline//.
 <code c++ Rectangle.cpp> <code c++ Rectangle.cpp>
Line 52: Line 56:
 Εάν δεν οριστεί κανένας κατασκευαστής σε μία κλάση τότε για την κλάση ορίζεται από τον μεταγλωττιστή ο //default// κατασκευαστής. Ο κατασκευαστής αυτός δεν έχει ορίσματα. Τα πεδία της κλάσης αρχικοποιούνται ως εξής: Εάν δεν οριστεί κανένας κατασκευαστής σε μία κλάση τότε για την κλάση ορίζεται από τον μεταγλωττιστή ο //default// κατασκευαστής. Ο κατασκευαστής αυτός δεν έχει ορίσματα. Τα πεδία της κλάσης αρχικοποιούνται ως εξής:
   - για πεδία που έχουν βασικού τύπους (int, double, char κλπ) οι τιμές είναι τυχαίες.   - για πεδία που έχουν βασικού τύπους (int, double, char κλπ) οι τιμές είναι τυχαίες.
-  - για πεδία αναφορικού τύπου καλείται ο default κατασκευαστής της εκάστοτε κλάσης.+  - για πεδία που περιγράφονται από κλάσεις καλείται ο //default// κατασκευαστής της εκάστοτε κλάσης.
  
-Εάν έχετε δηλώσει τουλάχιστον ένα κατασκευαστή που δεν είναι ο //default// κατασκευαστής χωρίς παραμέτρους τότε ο μεταγλωττιστής δεν κατασκευάζει αυτόματα τον //default// κατασκευαστή. Σε περίπτωση που ο //default// κατασκευαστής απαιτείται λαμβάνετε μήνυμα λάθους από τον μεταγλωττιστή.+Εάν έχετε δηλώσει τουλάχιστον ένα κατασκευαστή που δεν είναι ο //default// κατασκευαστής χωρίς παραμέτρους τότε ο //compiler// δεν κατασκευάζει αυτόματα τον //default// κατασκευαστή. Σε περίπτωση που ο //default// κατασκευαστής δεν δημιουργηθεί αυτόματα από τον //compiler//, λαμβάνετε μήνυμα λάθους κατά τη μεταγλώττιση.
  
 ===== Υπερφόρτωση κατασκευαστών ===== ===== Υπερφόρτωση κατασκευαστών =====
  
-Σε αναλογία με την [[cpp:functions#υπερφόρτωση_συναρτήσεων|υπερφόρτωση συναρτήσεων]] η C++ υποστηρίζει και υπερφόρτωση κατασκευαστών. Συγκεκριμένα, μπορείτε να έχετε περισσότερους από έναν καστασκευαστές αρκεί αυτοί να έχουν διαφορετικά ορίσματα. Δείτε το παρακάτω παράδειγμα της κλάση //Rectangle//, όπου δίνονται δύο επιπλέον κατασκευαστές, ένα κατασκευαστή που λαμβάνει ένα κοινό όρισμα και για τις δύο διαστάσεις του παραλληλογράμμου και ένα κατασκευαστή χωρίς ορίσματα που αναθέτει τυχαίες τιμές στις διαστάσεις του παραλληλογράμμου.+Σε αναλογία με την [[cpp:functions#υπερφόρτωση_συναρτήσεων|υπερφόρτωση συναρτήσεων]] η C++ υποστηρίζει και υπερφόρτωση κατασκευαστών. Συγκεκριμένα, μπορείτε να έχετε περισσότερους από έναν κατασκευαστές αρκεί αυτοί να έχουν διαφορετικό αριθμό ή τύπο ορισμάτων. Δείτε το παρακάτω παράδειγμα της κλάση //Rectangle//, όπου δίνονται δύο επιπλέον κατασκευαστές, ένα κατασκευαστή που λαμβάνει ένα κοινό όρισμα και για τις δύο διαστάσεις του παραλληλογράμμου και ένα κατασκευαστή χωρίς ορίσματα που αναθέτει τυχαίες τιμές στις διαστάσεις του παραλληλογράμμου.
  
-<code c++ Rectangle.cpp>+<code cpp Rectangle.hpp>
 #include <iostream> #include <iostream>
 #include <cstdlib> #include <cstdlib>
Line 75: Line 79:
     void setWidth(int w);     void setWidth(int w);
     void setHeight(int h);     void setHeight(int h);
-    int getWidth(); +    int getWidth() const
-    int getHeight();+    int getHeight() const;
 }; };
  
Line 94: Line 98:
 void Rectangle::setWidth(int w) { width = w; } void Rectangle::setWidth(int w) { width = w; }
 void Rectangle::setHeight(int h) { height = h; } void Rectangle::setHeight(int h) { height = h; }
-int Rectangle::getWidth() { return width; } +int Rectangle::getWidth() const { return width; } 
-int Rectangle::getHeight() { return height; }+int Rectangle::getHeight() const { return height; } 
 + 
 +</code> 
 + 
 +<code cpp RectangleUsage.cpp> 
 +#include "Rectangle.hpp"
  
 int main () { int main () {
Line 107: Line 116:
  
 Παραπάνω συναντήσαμε τον κατασκευαστή με δύο ορίσματα  Παραπάνω συναντήσαμε τον κατασκευαστή με δύο ορίσματα 
-<code c++>+<code cpp>
 Rectangle::Rectangle(int w, int h) { Rectangle::Rectangle(int w, int h) {
   width = w; height = h;   width = w; height = h;
Line 114: Line 123:
  
 Αντί για την παραπάνω δήλωση θα μπορούσατε να γράψετε ισοδύναμα: Αντί για την παραπάνω δήλωση θα μπορούσατε να γράψετε ισοδύναμα:
-<code c++>+<code cpp>
 Rectangle::Rectangle(int w, int h) : width(w) { Rectangle::Rectangle(int w, int h) : width(w) {
   height = h;   height = h;
Line 120: Line 129:
 </code> </code>
 ή ή
-<code c++>+<code cpp>
 Rectangle::Rectangle(int w, int h) : width(w), height(h) {} Rectangle::Rectangle(int w, int h) : width(w), height(h) {}
 </code> </code>
  
-Ο παραπάνω τρόπος αρχικοποίησης μπορεί να επεκταθεί και να χρησιμοποιηθεί για την κλήση κατασκευαστών των πεδίων της κλάσης, όταν αυτά είναι αναφορικού τύπου. Για παράδειγμα, δείτε παρακάτω την κλάση //Cuboid// η οποία διαθέτει ένα πεδίο της κλάσης //Rectangle//+Τελικά η κλάση Rectangle μπορεί να γραφεί ως εξής:
  
-<code c++ Cuboid.cpp>+<code cpp Rectangle.hpp>
 #include <iostream> #include <iostream>
 +#include <cstdlib>
 +#include <ctime>
 using namespace std; using namespace std;
  
-#include "Rectangle.cpp"+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; } 
 +</code> 
 + 
 +===== Κλήση ενός κατασκευαστή μέσα από άλλο κατασκευαστή ===== 
 + 
 +Συχνά μπορεί να θέλουμε να καλέσουμε μέσα από ένα κατασκευαστή έναν άλλο κατασκευαστή. Μπορείτε να το κάνετε αυτό χρησιμοποιώντας ως οποιαδήποτε άλλη μέθοδο. Στο προηγούμενο παράδειγμα, ο κατασκευαστής με τη μία παράμετρο καλεί εσωτερικά τον κατασκευαστή με τις δύο παραμέτρους ως εξής: 
 + 
 +<code cpp> 
 +Rectangle::Rectangle(int s) { 
 +  Rectangle(s,s); 
 +
 +</code> 
 + 
 +O ίδιος κώδικας με χρήση //initialization list// μπορεί να γραφεί ως εξής: 
 + 
 +<code cpp> 
 +Rectangle::Rectangle(int s) : Rectangle(s,s) { 
 +
 +</code> 
 + 
 +===== Αρχικοποίηση αντικειμένων που περιέχουν πεδία που περιγράφονται από κλάσεις ===== 
 + 
 +Στην προηγούμενη ενότητα είδαμε την κλάση Cuboid που περιγράφει την κλάση του κυβοειδούς και περιέχει ένα πεδίο τύπου Rectangle. Εδώ θα δούμε πως μπορούμε να ορίσουμε ένα ή περισσότερους κατασκευαστές για τη συγκεκριμένη κλάση. 
 + 
 +<code cpp Cuboid.hpp> 
 +#include <iostream> 
 +using namespace std; 
 + 
 +#include "Rectangle.hpp"
  
 class Cuboid { class Cuboid {
Line 137: Line 203:
     Rectangle rect;     Rectangle rect;
   public:   public:
-    Cuboid(Rectangle r, int l); 
     Cuboid(int w, int h, int l);     Cuboid(int w, int h, int l);
-    void setRectangle(Rectangle r) {rect = r;} +    Cuboid(Rectangle r, int l); 
-    Rectangle getRectangle() {return rect;} +     
-    void setLength(int l) { length = l} +    void setRectangle(Rectangle r); 
-    int getLength() { return length}+    Rectangle getRectangle() const
 +    void setLength(int l); 
 +    int getLength() const;
     int volume();     int volume();
 }; };
 +</code>
  
-Cuboid::Cuboid(Rectangle r, int l) : rect(r), length(l) { } +<code cpp Cuboid.cpp> 
-Cuboid::Cuboid(int w, int h, int l) : rect(w,h), length(l) { }+#include "Cuboid.hpp"
  
-int Cuboid::volume() { +Cuboid::Cuboid(int w, int h, int l 
-  return length * rect.getWidth() rect.getHeight();+  rect.setWidth(w)
 +  rect.setHeight(h)
 +  length = l;
 } }
  
-int main () { +Cuboid::Cuboid(Rectangle r, int l) { 
-  Rectangle rect(5,6); +   rect = r; 
-  Cuboid cuboid1(rect, 10); +   length = l; 
-  cout << "volume" << cuboid1.volume() << endl+
-  Cuboid cuboid2(2,3,4); + 
-  cout << "volume" << cuboid2.volume() << endl; +void Cuboid::setRectangle(Rectangle r{rect = r;} 
-  return 0;+Rectangle Cuboid::getRectangle() const {return rect;} 
 +void Cuboid::setLength(int l{ length = l} 
 +int Cuboid::getLength() const { return length} 
 + 
 +int Cuboid::volume() { 
 +  return length * rect.getWidth() * rect.getHeight();
 } }
 </code> </code>
  
-Παρατηρήστε πως ορίζονται οι κατασκευαστές της κλάσης //Cuboid// με χρήση //member initialization list//. +Οι παραπάνω δύο κατασκευαστές με χρήση member initialιzation list μπορούν να γραφούν ως εξής: 
 <code> <code>
 Cuboid::Cuboid(Rectangle r, int l) : rect(r), length(l) { } Cuboid::Cuboid(Rectangle r, int l) : rect(r), length(l) { }
Line 169: Line 245:
 </code> </code>
  
-Στην 1η περίπτωση γίνεται ανάθεση του αντικειμένου //r// στο πεδίο //rect//. Στην 2η περίπτωση καλείται ο κατασκευαστής της κλάσης //Rectangle// με ορίσματα //(w,h)//Για να μεταγλωττίσετε και να εκτελέσετε το παραπάνω πρόγραμμα βάλτε σε σχόλια τη μέθοδο //main// της κλάσης Rectangle.+Στην 1η περίπτωση γίνεται ανάθεση του αντικειμένου //r// στο πεδίο //rect//. Στην 2η περίπτωση καλείται ο κατασκευαστής της κλάσης //Rectangle// με ορίσματα //(w,h)//. 
 + 
 +Η ανάθεση ''rect = r;'' δεν είναι προφανής. Έχοντας δύο αντικείμενα τύπου Rectangle το ''rect'' και το ''r'', η εντολή ανάθεσης αντιγράφει τα περιεχόμενα του ''r'' στο ''rect'' πεδίο προς πεδίο. 
 ===== Καταστροφέας της κλάσης ===== ===== Καταστροφέας της κλάσης =====
  
Line 176: Line 255:
 Στο παρακάτω παράδειγμα βλέπετε την κλάση //Rectangle// όπου τα πεδία width και height είναι δείκτες. Σε αυτή την περίπτωση θα πρέπει να δεσμεύουμε την απαραίτητη μνήμη με χρήση του τελεστή new. Στον καταστροφέα την κλάσης απελευθερώνουμε τη μνήμη που δεσμεύσαμε προηγούμενα. Στο παρακάτω παράδειγμα βλέπετε την κλάση //Rectangle// όπου τα πεδία width και height είναι δείκτες. Σε αυτή την περίπτωση θα πρέπει να δεσμεύουμε την απαραίτητη μνήμη με χρήση του τελεστή new. Στον καταστροφέα την κλάσης απελευθερώνουμε τη μνήμη που δεσμεύσαμε προηγούμενα.
  
-<code c++ Rectangle.cpp>+<code cpp Rectangle.cpp>
 #include <iostream> #include <iostream>
 #include <cstdlib> #include <cstdlib>
Line 201: Line 280:
   }   }
   *width = w; *height = h;   *width = w; *height = h;
 +  cout << "Constructing rectangle (w:"<< *width <<", h:"<<*height<<")\n";
 } }
  
 Rectangle::~Rectangle() { Rectangle::~Rectangle() {
 +  cout << "Destructing rectangle (w:"<< *width <<", h:"<<*height<<")\n";
   delete width;   delete width;
   delete height;   delete height;
cpp/constructors_destructors.1492675867.txt.gz · Last modified: 2017/04/20 07:11 (external edit)