User Tools

Site Tools


cpp:copy_constructors

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
Next revisionBoth sides next revision
cpp:copy_constructors [2019/04/19 10:01] – [Μια πιο σύνθετη περίπτωση] gthanoscpp:copy_constructors [2020/04/14 15:27] – [Ορισμός ενός κατασκευαστή αντιγραφέα] gthanos
Line 1: Line 1:
 ====== Κατασκευαστές Αντιγραφείς ====== ====== Κατασκευαστές Αντιγραφείς ======
  
-Στην ενότητα των συναρτήσεων είδαμε [[cpp:functions#κλήση_με_τιμή_και_κλήση_με_αναφορά|την κλήση με τιμή και κλήση με αναφορά]] προκειμένου να περάσουμε παραμέτρους σε μία συνάρτηση. Κατά την κλήση με τιμή όταν η παράμετρος είναι ένα αντικείμενο, ένα αντίγραφο του αντικειμένου θα πρέπει να δημιουργηθεί στο //stack// της συνάρτησης που καλείται. Προκειμένου να γίνει αυτό η C++ ορίζει την έννοια του κατασκευαστή αντιγραφέα (//copy constructor//), ο οποίος δημιουργεί ένα αντικείμενο που είναι ακριβές αντίγραφο ενός άλλου αντικειμένου. Δείτε το παρακάτω παράδειγμα της μεθόδου //printArea// η οποία λαμβάνει ως παράμετρο ένα αντικείμενο της κλάσης //Rectangle//.+Στην ενότητα των συναρτήσεων είδαμε [[cpp:functions#κλήση_με_τιμή_και_κλήση_με_αναφορά|την κλήση με τιμή και κλήση με αναφορά]] προκειμένου να περάσουμε παραμέτρους σε μία συνάρτηση. Κατά **την κλήση με τιμή** όταν η παράμετρος είναι ένα αντικείμενο, ένα αντίγραφο του αντικειμένου θα πρέπει να δημιουργηθεί στο //stack// της συνάρτησης που καλείται. Προκειμένου να γίνει αυτό η C++ ορίζει την έννοια του κατασκευαστή αντιγραφέα (//copy constructor//), ο οποίος δημιουργεί ένα αντικείμενο που είναι ακριβές αντίγραφο ενός άλλου αντικειμένου του ίδου τύπου.  
 + 
 +Δείτε το παρακάτω παράδειγμα της μεθόδου //printArea// η οποία λαμβάνει ως παράμετρο ένα αντικείμενο της κλάσης //Rectangle//. 
 + 
 +<code cpp 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(); 
 +    int getHeight(); 
 +}; 
 + 
 +Rectangle::Rectangle(int w, int h) { 
 +  cout << "Calling 2 args constructor" << endl; 
 +  width = w; height = h; 
 +
 + 
 +Rectangle::Rectangle(int s) : { 
 +  cout << "Calling 1 args constructor" << endl; 
 +  width = s; height = s; 
 +
 + 
 +Rectangle::Rectangle() { 
 +  cout << "Calling default constructor" << endl; 
 +  width = height = 0; 
 +
 + 
 +void Rectangle::setWidth(int w) { width = w; } 
 +void Rectangle::setHeight(int h) { height = h; } 
 +int Rectangle::getWidth() { return width; } 
 +int Rectangle::getHeight() { return height; } 
 +</code>
  
 <code cpp foo.cpp> <code cpp foo.cpp>
 #include <iostream> #include <iostream>
 using namespace std; using namespace std;
-#include "Rectangle.cpp"+#include "Rectangle.hpp"
  
 void printArea(const Rectangle r) { void printArea(const Rectangle r) {
Line 21: Line 63:
  
 {{ :cpp:cppstackcopyconstructor.png |}} {{ :cpp:cppstackcopyconstructor.png |}}
 +
 +Το ερώτημα είναι με ποιό τρόπο γίνεται η δημιουργία του αντιγράφου του αρχικού αντικειμένου στο //stack//. Μπορείτε να δηλώσετε τον δικό σας κατασκευαστή αντιγραφέα (λεπτομέρειες πιο κάτω..) ή να αφήσετε τον //compiler// να δηλώσει τον //default//. Ο //default// κατασκευαστής αντιγραφέας δημιουργεί το νέο αντικείμενο από το παλιό αντιγράφοντας τα πεδία πεδίο προς πεδιο. Εάν αυτός ο τρόπος σας ικανοποιεί, τότε δεν χρειάζεται να κάνετε κάτι περισσότερο. Εάν όμως η παραπάνω μεθοδολογία δεν είναι ικανοποιητική, είστε αναγκασμένοι να ορίσετε τον δικό σας κατασκευαστή αντιγραφέα.
  
 ===== Ορισμός ενός κατασκευαστή αντιγραφέα ===== ===== Ορισμός ενός κατασκευαστή αντιγραφέα =====
  
-Ένας κατασκευαστής αντιγραφέας για την κλάση Rectangle θα μπορούσε να είναι ο εξής:+Ένας κατασκευαστής αντιγραφέας για την παραπάνω κλάση //Rectangle// θα μπορούσε να είναι ο εξής:
  
 <code cpp> <code cpp>
-Rectangle::Rectangle(const Rectangle &r) { +Rectangle::Rectangle(Rectangle &r) { 
-  width = r.width; height = r.height;+  width = r.width;  
 +  height = r.height;
 } }
 </code> </code>
Line 34: Line 79:
 <code cpp> <code cpp>
 Rectangle::Rectangle(const Rectangle &r) { Rectangle::Rectangle(const Rectangle &r) {
-  width = r.width; height = r.height;+  width = r.width;  
 +  height = r.height;
 } }
 </code> </code>
  
-Στη 2η περίπτωση, η μεταβλητή //r// δηλώνεται ως //const// διότι κατά την εκτέλεση του κατασκευαστή αντιγραφέα το αντικείμενο //r// δεν μεταβάλλεται. Συνολικά η κλάση //Rectangle// διαμορφώνεται ως εξής:+Και οι δύο παραπάνω κατασκευαστές δηλώνουν ένα κατασκευαστή αντιγραφέα. Στη δεύτερη περίπτωση, η μεταβλητή //r// δηλώνεται ως //const// διότι κατά την εκτέλεση του κατασκευαστή αντιγραφέα το αντικείμενο //r// δεν μεταβάλλεται. Συνολικά η κλάση //Rectangle// διαμορφώνεται ως εξής:
  
 <code cpp Rectangle.hpp> <code cpp Rectangle.hpp>
Line 53: Line 99:
     Rectangle(int s);     Rectangle(int s);
     Rectangle();     Rectangle();
-    Rectangle(Rectangle &r);+    Rectangle(Rectangle& r);
     void setWidth(int w);     void setWidth(int w);
     void setHeight(int h);     void setHeight(int h);
Line 65: Line 111:
 } }
  
-Rectangle::Rectangle(int s) : Rectangle(s,s) {+Rectangle::Rectangle(int s) {
   cout << "Calling 1 args constructor" << endl;   cout << "Calling 1 args constructor" << endl;
 +  width = s; height = s;
 } }
  
-Rectangle::Rectangle() : Rectangle(0,0) {+Rectangle::Rectangle() {
   cout << "Calling default constructor" << endl;   cout << "Calling default constructor" << endl;
 +  width = height = 0;
 } }
  
-Rectangle::Rectangle(Rectangle &r) {+Rectangle::Rectangle(Rectangle& r) {
   cout << "Calling copy constructor" << endl;   cout << "Calling copy constructor" << endl;
-  width = r.width; height = r.height;+  width = r.width; 
 +  height = r.height;
 } }
  
Line 82: Line 131:
 int Rectangle::getWidth() { return width; } int Rectangle::getWidth() { return width; }
 int Rectangle::getHeight() { return height; } int Rectangle::getHeight() { return height; }
- 
-void printArea(Rectangle r) { 
-  cout << " area: " << r.getWidth() * r.getHeight() << endl; 
-} 
 </code> </code>
  
Line 97: Line 142:
  
 <code cpp CopyRectangle.cpp> <code cpp CopyRectangle.cpp>
-#include "Rectangle.cpp"+#include "Rectangle.hpp"
  
 int main() { int main() {
Line 108: Line 153:
 Το παραπάνω είναι λειτουργικά ισοδύναμο με το παρακάτω. Το παραπάνω είναι λειτουργικά ισοδύναμο με το παρακάτω.
 <code cpp CopyRectangle.cpp> <code cpp CopyRectangle.cpp>
-#include "Rectangle.cpp"+#include "Rectangle.hpp"
  
 int main() { int main() {
Line 123: Line 168:
 Στις περιπτώσεις που υπάρχουν πεδία δείκτες που δείχνουν σε άλλα αντικείμενα (στατικά ή δυναμικά δεσμευμένα) αντιγράφονται οι διευθύνσεις αυτές, όπως θα αντιγράφονταν οποιοδήποτε άλλο πεδίο. Αυτό πρακτικά σημαίνει ότι δύο ή περισσότερα αντικείμενα δείχνουν σε μία κοινή περιοχή μνήμης. Το παραπάνω μπορεί να προκαλέσει δυσλειτουργίες, καθώς η μεταβολή του κοινού αντικειμένου επηρεάζει το σύνολο των αντικειμένων που το μοιράζονται. Στις περιπτώσεις που υπάρχουν πεδία δείκτες που δείχνουν σε άλλα αντικείμενα (στατικά ή δυναμικά δεσμευμένα) αντιγράφονται οι διευθύνσεις αυτές, όπως θα αντιγράφονταν οποιοδήποτε άλλο πεδίο. Αυτό πρακτικά σημαίνει ότι δύο ή περισσότερα αντικείμενα δείχνουν σε μία κοινή περιοχή μνήμης. Το παραπάνω μπορεί να προκαλέσει δυσλειτουργίες, καθώς η μεταβολή του κοινού αντικειμένου επηρεάζει το σύνολο των αντικειμένων που το μοιράζονται.
  
-Στο παρακάτω παράδειγμα ορίζουμε την κλάση //Point// η οποία αντιπροσωπεύει ένα σημείο στο διδιάστατο χώρο.+Στο παρακάτω παράδειγμα ορίζουμε την κλάση //Point// η οποία αντιπροσωπεύει ένα σημείο στο δισδιάστατο χώρο.
  
 <code cpp Point.hpp> <code cpp Point.hpp>
Line 156: Line 201:
 </code> </code>
  
-Η κλάση //Rectangle// που ακολουθεί ορίζει ένα πεδίο δείκτη σε αντικείμενα τύπου //Point//. Η δημιουργία ενός αντικειμένου τύπου //Rectangle// συνεπάγεται τη δυναμική δέσμευση μνήμης για το αντικείμενο τύπου //Point// που αυτή περιέχει. Δείτε το παράδειγμα που ακολουθεί.+Η κλάση //Rectangle// που ακολουθεί ορίζει το πεδίο //origin// που είναι δείκτης σε ένα αντικείμενο τύπου //Point//. Η δημιουργία ενός αντικειμένου τύπου //Rectangle// συνεπάγεται τη δυναμική δέσμευση μνήμης για το αντικείμενο τύπου //Point// που αυτή περιέχει. Δείτε το παράδειγμα που ακολουθεί.
  
 <code cpp Rectangle.hpp> <code cpp Rectangle.hpp>
cpp/copy_constructors.txt · Last modified: 2022/05/12 19:41 by gthanos