sir ImPeCaBlE Δημοσ. 10 Ιουλίου 2020 Δημοσ. 10 Ιουλίου 2020 (επεξεργασμένο) Έχω φάει ένα σκαλωματάκι σε κάτι πολύ βασικό αλλά δε βγάζω άκρη. Φτιάχνω μια κλάση που τα fields της είναι ένας πίνακας από strings, το μέγεθος του πίνακα και ο αριθμός των θέσεων του πίνακα που έχουν "γεμίσει". Έχω έναν constructor με είσοδο το επιθυμητό μέγεθος του πίνακα, έναν copy constructor και ένα destructor. Από συναρτήσεις έχω την add που βάζει ένα string στον πίνακα αν υπάρχει άδεια θέση και την print που φτιάχνει ένα string με τα περιεχόμενα του πίνακα. Ως εδώ όλα καλά (νομίζω δηλαδή ). Spoiler #include <iostream> using namespace std; class ArrayTest { private: int size; int currentSize; string *table; public: ArrayTest(int size = 5); ArrayTest(const ArrayTest &ct); bool add(string str); ArrayTest operator + (const string str); ~ArrayTest(); string print(); }; ArrayTest::ArrayTest(int size){ this->size = size; currentSize = 0; table = new string[size]; } ArrayTest::ArrayTest(const ArrayTest &ct){ size = ct.size; currentSize = ct.currentSize; table = new string[size]; for(int i=0; i<ct.currentSize; i++){ table[i] = ct.table[i]; } } ArrayTest::~ArrayTest(){ cout<<"deleting object\n"; delete []table; } bool ArrayTest::add(string str){ if(size==currentSize){ return false; } table[currentSize++] = str; return true; } string ArrayTest::print(){ string str; char buf[128]; for(int i=0; i<currentSize;i++){ sprintf(buf, "%d. %s\n", i, table[i].c_str()); str.append(buf); } return str; } Το πρόβλημα είναι εδώ: θέλω να κάνω overload το "+" ώστε η πράξη <ArrayTest + "string"> να επιστρέφει ένα νέο αντικείμενο της κλάσης που να περιέχει το "string" στο πίνακά του (αν υπάρχει χώρος). Επειδή πρώτη φορά δουλεύω c++, έγραψα τον παρακάτω κώδικα τελείως πρόχειρα για να δω αν δουλεύει. Δούλευε οπότε το άφησα. Τώρα που το κοιτάω πιο προσεκτικά δε καταλαβαίνω γιατί δουλεύει. ArrayTest ArrayTest::operator + (const string str){ ArrayTest n = ArrayTest(*this); n.add(str); return n; } Επιστρέφω ένα αντικείμενο που έχει δημιουργηθεί μέσα στη συνάρτηση, οπότε περιμένω όταν βγω από τη συνάρτηση, αυτό να σταματήσει να υπάρχει. Δηλαδή αν το είχα γράψει πιο προσεκτικά θα είχα κάνει κάτι τέτοιο ArrayTest* ArrayTest::operator + (const string str){ ArrayTest *n = new ArrayTest(*this); n->add(str); return n; } Ωστόσο ο αρχικός κώδικας δουλεύει. Τι έχω καταλάβει λάθος? Επεξ/σία 10 Ιουλίου 2020 από sir ImPeCaBlE
albNik Δημοσ. 10 Ιουλίου 2020 Δημοσ. 10 Ιουλίου 2020 (επεξεργασμένο) Δεν χαίρεσαι που δουλευει; 😀 Λογικα επιστρεφεις ενα copy του ArrayTest n και δουλεύει επειδή εχεις υλοποιήσει το copy constructor. https://stackoverflow.com/questions/665781/copy-constructor-in-c-is-called-when-object-is-returned-from-a-function Επεξ/σία 10 Ιουλίου 2020 από albNik 1
kaliakman Δημοσ. 10 Ιουλίου 2020 Δημοσ. 10 Ιουλίου 2020 (επεξεργασμένο) Για να καταλάβεις ακριβώς τι συμβαίνει βάλε print statements να δεις τι καλείται σε κάθε construction. Πρακτικά ναι καλείται ο copy constructor. Βέβαια αναλόγως την έκδοση που τρέχεις μπορεί να γίνονται ωραία πραγματάκια όπως copy elision αλλά άστο προς το παρόν 😃 Επεξ/σία 10 Ιουλίου 2020 από kaliakman 1
sir ImPeCaBlE Δημοσ. 10 Ιουλίου 2020 Μέλος Δημοσ. 10 Ιουλίου 2020 Όσο το ψάχνω τόσο μπερδεύομαι. 22 λεπτά πριν, albNik είπε Δεν χαίρεσαι που δουλευει; 😀 Λογικα επιστρεφεις ενα copy του ArrayTest n και δουλεύει επειδή εχεις υλοποιήσει το copy constructor. https://stackoverflow.com/questions/665781/copy-constructor-in-c-is-called-when-object-is-returned-from-a-function Thanx για την απάντηση. Ο copy constructor μου καλείται μια φορά για το ArrayTest n = ArrayTest(*this); Δε τον βλέπω να ξανακαλείται από κάτι άλλο. int main(){ ArrayTest t = ArrayTest(6); t.add("first"); t.add("second"); t.add("third"); ArrayTest k = (t+"fourth"); cout<<&k<<"\n"; cout<<k.print(); } Αυτό μου έβγαλε ότι η διεύθυνση του k είναι η ίδια με τη διεύθυνση του "n" μέσα στην "operator +". Πωτς γκενεν αυτό?
albNik Δημοσ. 10 Ιουλίου 2020 Δημοσ. 10 Ιουλίου 2020 (επεξεργασμένο) 22 λεπτά πριν, sir ImPeCaBlE είπε Όσο το ψάχνω τόσο μπερδεύομαι. Thanx για την απάντηση. Ο copy constructor μου καλείται μια φορά για το ArrayTest n = ArrayTest(*this); Δε τον βλέπω να ξανακαλείται από κάτι άλλο. int main(){ ArrayTest t = ArrayTest(6); t.add("first"); t.add("second"); t.add("third"); ArrayTest k = (t+"fourth"); cout<<&k<<"\n"; cout<<k.print(); } Αυτό μου έβγαλε ότι η διεύθυνση του k είναι η ίδια με τη διεύθυνση του "n" μέσα στην "operator +". Πωτς γκενεν αυτό? To return n; στο + operator φτιαχνει ενα copy του n μόλις πριν το επιστρέψει. Ειναι οπως λέμε pass by value , return by value. Mπορεις να επαληθεύσεις οτι στο return n; καλειται ο copy constructor. ArrayTest t = ArrayTest(6); //εδω δεν καλειται ο copy constructor t+"1"+"2"+"3" +"4"+"5"; //εδω καλειται 5 φορες ο copy constructor Επεξ/σία 10 Ιουλίου 2020 από albNik
sir ImPeCaBlE Δημοσ. 10 Ιουλίου 2020 Μέλος Δημοσ. 10 Ιουλίου 2020 8 λεπτά πριν, albNik είπε To return n; στο + operator φτιαχνει ενα copy του n μόλις πριν το επιστρέψει. Ειναι οπως λέμε pass by value , return by value. Mπορεις να επαληθεύσεις οτι στο return n; καλειται ο copy constructor. Δεν τον βλέπω να καλείται εκεί. Μια φορά καλείται στο ArrayTest n = ArrayTest(*this); Δε νομίζω ότι φτιάχνει αντίγραφο, 1ον δεν χρησιμοποιεί τον copy constructor μου, 2ον το n μέσα στη συνάρτηση έχει την ίδια διεύθυνση με το αντικείμενο στη main. Αυτός ο κώδικας δηλαδή (που το k παίρνει άλλη διεύθυνση) δε δουλεύει. int main(){ ArrayTest t = ArrayTest(6); t.add("first"); t.add("second"); t.add("third"); /**********************************/ ArrayTest k; k = (t+"fourth"); /**********************************/ cout<<"main "<<&k<<"\n"; cout<<k.print(); } Το αντικείμενο που επιστρέφεται από την "operator +" διαγράφεται.
albNik Δημοσ. 10 Ιουλίου 2020 Δημοσ. 10 Ιουλίου 2020 Προσθεσε std::cout << "Copy constructor"; μεσα στο copy constructor. Ποσες φορες εκτυπωνεται το "Copy Constructor" στο παρακατω ; ArrayTest::ArrayTest(const ArrayTest &ct){ std::cout << "Copy Constructor"; size = ct.size; currentSize = ct.currentSize; table = new string[size]; for(int i=0; i<ct.currentSize; i++){ table[i] = ct.table[i]; } } ArrayTest t = ArrayTest(6); //εδω δεν καλειται ο copy constructor t+"1"+"2"+"3" +"4"+"5"; //εδω καλειται 5 φορες ο copy constructor
sir ImPeCaBlE Δημοσ. 10 Ιουλίου 2020 Μέλος Δημοσ. 10 Ιουλίου 2020 (επεξεργασμένο) 6 λεπτά πριν, albNik είπε Προσθεσε std::cout << "Copy constructor"; μεσα στο copy constructor. Ποσες φορες εκτυπωνεται το "Copy Constructor" στο παρακατω ; ArrayTest::ArrayTest(const ArrayTest &ct){ std::cout << "Copy Constructor"; size = ct.size; currentSize = ct.currentSize; table = new string[size]; for(int i=0; i<ct.currentSize; i++){ table[i] = ct.table[i]; } } ArrayTest t = ArrayTest(6); //εδω δεν καλειται ο copy constructor t+"1"+"2"+"3" +"4"+"5"; //εδω καλειται 5 φορες ο copy constructor Ναι συμφωνώ αλλά νομίζω καλείται για την είσοδο της συνάρτησης όχι για την επιστροφή του αποτελέσματος. Δηλαδή αντιγράφει τα περιεχόμενα του αντικειμένου που παίρνει σαν όρισμα (το "t", το "t+1" κτλ) στο εσωτερικό αντικείμενο της συνάρτησης και στη συνέχεια προσθέτει στο τελευταίο το νέο string. Αυτό που γυρίζει όμως (ότι είναι αυτό τελικά) δε χρησιμοποιεί τον copy constructor. Επεξ/σία 10 Ιουλίου 2020 από sir ImPeCaBlE
albNik Δημοσ. 10 Ιουλίου 2020 Δημοσ. 10 Ιουλίου 2020 (επεξεργασμένο) Τοτε πως εξηγείς το οτι εκτύπωσε 5 φορες "Copy Constructor"; t+"1" σημαινει ArrayTest ArrayTest::operator + (const string str) ο οποιος εχει μια return n; Εκει γινεται το copy ArrayTest t = ArrayTest(6); t+"1"+"2"+"3"+"4"+"5"; Αναφορά σε κείμενο Αυτό που γυρίζει όμως (ότι είναι αυτό τελικά) δε χρησιμοποιεί τον copy constructor. Οποιαδήποτε συνάρτηση (επομένως και η +operator) επιστρέφει ArrayTest καλει τον copy constructor. ArrayTest::Myfun(){ ArrayList a= ArrayList(); return a; //Copy Constructor } Επεξ/σία 10 Ιουλίου 2020 από albNik
sir ImPeCaBlE Δημοσ. 10 Ιουλίου 2020 Μέλος Δημοσ. 10 Ιουλίου 2020 28 λεπτά πριν, albNik είπε Τοτε πως εξηγείς το οτι εκτύπωσε 5 φορες "Copy Constructor"; t+"1" σημαινει ArrayTest ArrayTest::operator + (const string str) ο οποιος εχει μια return n; Εκει γινεται το copy ArrayTest t = ArrayTest(6); t+"1"+"2"+"3"+"4"+"5"; Οποιαδήποτε συνάρτηση (επομένως και η +operator) επιστρέφει ArrayTest καλει τον copy constructor. ArrayTest::Myfun(){ ArrayList a= ArrayList(); return a; //Copy Constructor } Μα o copy constructor καλείται 5 φορές explicitly για την αντιγραφή του καλούντος αντικειμένου στο εσωτερικό. Αν καλείται και για την αντιγραφή του εσωτερικού αντικειμένου στο αποτέλεσμα, δε θα έπρεπε να δούμε αυτό το μήνυμα άλλες τόσες φορές? Αν στη operator+ βάλω αυτά τα μηνύματα ArrayTest ArrayTest::operator + (const string str){ ArrayTest n = ArrayTest(*this); cout<<"copied\n"; n.add(str); cout<<"add\n"; return n; } η έξοδος είναι Copy Constructor copied add Copy Constructor copied add Copy Constructor copied add Copy Constructor copied add Copy Constructor copied add Ούτε ο κώδικας που έγραψες δε μου δείχνει ότι καλεί τον copy constructor.
albNik Δημοσ. 10 Ιουλίου 2020 Δημοσ. 10 Ιουλίου 2020 Μόλις τώρα, sir ImPeCaBlE είπε Μα o copy constructor καλείται 5 φορές explicitly για την αντιγραφή του καλούντος αντικειμένου στο εσωτερικό. Αν καλείται και για την αντιγραφή του εσωτερικού αντικειμένου στο αποτέλεσμα, δε θα έπρεπε να δούμε αυτό το μήνυμα άλλες τόσες φορές? Αν στη operator+ βάλω αυτά τα μηνύματα ArrayTest ArrayTest::operator + (const string str){ ArrayTest n = ArrayTest(*this); cout<<"copied\n"; n.add(str); cout<<"add\n"; return n; } η έξοδος είναι Copy Constructor copied add Copy Constructor copied add Copy Constructor copied add Copy Constructor copied add Copy Constructor copied add Ούτε ο κώδικας που έγραψες δε μου δείχνει ότι καλεί τον copy constructor. Mea Culpa. Αγνόησ(τ)ε όσα έγραψα στο νημα Εχω καιρο να ασχοληθώ με C++.
sir ImPeCaBlE Δημοσ. 10 Ιουλίου 2020 Μέλος Δημοσ. 10 Ιουλίου 2020 4 λεπτά πριν, albNik είπε Mea Culpa. Αγνόησ(τ)ε όσα έγραψα στο νημα Εχω καιρο να ασχοληθώ με C++. All good.
k33theod Δημοσ. 11 Ιουλίου 2020 Δημοσ. 11 Ιουλίου 2020 (επεξεργασμένο) 13 ώρες πριν, sir ImPeCaBlE είπε Επιστρέφω ένα αντικείμενο που έχει δημιουργηθεί μέσα στη συνάρτηση, οπότε περιμένω όταν βγω από τη συνάρτηση, αυτό να σταματήσει να υπάρχει. Δηλαδή αν το είχα γράψει πιο προσεκτικά θα είχα κάνει κάτι τέτοιο Αφού το επιστρέφεις και το κάνεις ανάθεση σε άλλο αντικείμενο γιατί να σταματήσει να υπάρχει; Στην 1η περίπτωση επιστρέφεις object στην δεύτερη pointer. Επεξ/σία 11 Ιουλίου 2020 από k33theod 1
sir ImPeCaBlE Δημοσ. 11 Ιουλίου 2020 Μέλος Δημοσ. 11 Ιουλίου 2020 (επεξεργασμένο) 3 ώρες πριν, k33theod είπε Αφού το επιστρέφεις και το κάνεις ανάθεση σε άλλο αντικείμενο γιατί να σταματήσει να υπάρχει; Στην 1η περίπτωση επιστρέφεις object στην δεύτερη pointer. Αυτό σκεφτόμουν και εγώ όταν το έγραφα γιατί ερχόμουν από java. Κολλάω στο εξής: η μνήμη για αυτό το αντικείμενο δεσμεύτηκε μέσα στη συνάρτηση. Άρα όταν βγω από τη συνάρτηση, δε θα αποδεσμευτεί? Θα καταλάβαινα να δούλευε αν είχα δεσμεύσει μνήμη ρητά με new/malloc (και μετά έπρεπε να κάνω delete/free) ή να έκανε copy τις τιμές αλλά δε χρησιμοποιεί τον copy constructor. Έβαλα και ένα -O0 στον g++ για να δω μήπως κάνει καμία περίεργη βελτιστοποίηση αλλά δεν άλλαξε κάτι. Επεξ/σία 11 Ιουλίου 2020 από sir ImPeCaBlE
Sacadmerde Δημοσ. 11 Ιουλίου 2020 Δημοσ. 11 Ιουλίου 2020 (επεξεργασμένο) 17 λεπτά πριν, sir ImPeCaBlE είπε η μνήμη για αυτό το αντικείμενο δεσμεύτηκε μέσα στη συνάρτηση. Άρα όταν βγω από τη συνάρτηση, δε θα αποδεσμευτεί? Όχι, εάν δεν το πεις εσύ. Δηλαδή, εάν μέσα σε μία μέθοδο κάνεις 10 new, τότε εάν δεν κάνεις delete, θα έχεις leaks. Για αυτό υπάρχει (πλέον) ο auto_ptr. Στην Java, αυτό τακτοποιείται με τον GC. Το πρόβλημα που βλέπεις με την ίδια διεύθυνση μνήμης, είναι γιατί στο post 14 ώρες πριν, sir ImPeCaBlE είπε Αυτό μου έβγαλε ότι η διεύθυνση του k είναι η ίδια με τη διεύθυνση του "n" μέσα στην "operator +". Πωτς γκενεν αυτό? προφανώς χρησιμοποιείς την έκδοση του CopyCstor με τον pointer αντί για την πρώτη έκδοση με την δημιουργία αντικειμένου. Η διαφορά με την Java, είναι ότι σε όλα (εκτός από τα primitive types `int` κτλ, αλλά όχι `Integer`) η Java χρησιμοποιεί by default, pointers. Η C++ όχι. Επεξ/σία 11 Ιουλίου 2020 από Sacadmerde
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα