Προς το περιεχόμενο

C++ και επιστροφή αντικειμένου από συνάρτηση


sir ImPeCaBlE

Προτεινόμενες αναρτήσεις

Δημοσ. (επεξεργασμένο)

Έχω φάει ένα σκαλωματάκι σε κάτι πολύ βασικό αλλά δε βγάζω άκρη.

Φτιάχνω μια κλάση που τα fields της είναι ένας πίνακας από strings, το μέγεθος του πίνακα και ο αριθμός των θέσεων του πίνακα που έχουν "γεμίσει". Έχω έναν constructor με είσοδο το επιθυμητό μέγεθος του πίνακα, έναν copy constructor και ένα destructor. Από συναρτήσεις έχω την add που βάζει ένα string στον πίνακα αν υπάρχει άδεια θέση και την print που φτιάχνει ένα string με τα περιεχόμενα του πίνακα. Ως εδώ όλα καλά (νομίζω δηλαδή :lol: ).

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;
}

Ωστόσο ο αρχικός κώδικας δουλεύει. Τι έχω καταλάβει λάθος?

Επεξ/σία από sir ImPeCaBlE
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

Δεν χαίρεσαι που δουλευει; 😀

Λογικα επιστρεφεις ενα copy του ArrayTest n και δουλεύει επειδή εχεις υλοποιήσει το copy constructor.

 

https://stackoverflow.com/questions/665781/copy-constructor-in-c-is-called-when-object-is-returned-from-a-function

 

Επεξ/σία από albNik
  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

Για να καταλάβεις ακριβώς τι συμβαίνει βάλε print statements να δεις τι καλείται σε κάθε construction. Πρακτικά ναι καλείται ο copy constructor. Βέβαια αναλόγως την έκδοση που τρέχεις μπορεί να γίνονται ωραία πραγματάκια όπως copy elision αλλά άστο προς το παρόν 😃

Επεξ/σία από kaliakman
  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Όσο το ψάχνω τόσο μπερδεύομαι.

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 +". Πωτς γκενεν αυτό?

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)
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

 

Επεξ/σία από albNik
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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 +" διαγράφεται.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Προσθεσε 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                                   
                                   

 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)
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.

Επεξ/σία από sir ImPeCaBlE
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

Τοτε πως εξηγείς το οτι εκτύπωσε 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
}

 

Επεξ/σία από albNik
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Μόλις τώρα, 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++. 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)
13 ώρες πριν, sir ImPeCaBlE είπε

Επιστρέφω ένα αντικείμενο που έχει δημιουργηθεί μέσα στη συνάρτηση, οπότε περιμένω όταν βγω από τη συνάρτηση, αυτό να σταματήσει να υπάρχει. Δηλαδή αν το είχα γράψει πιο προσεκτικά θα είχα κάνει κάτι τέτοιο

Αφού το επιστρέφεις και το κάνεις ανάθεση σε άλλο αντικείμενο γιατί να σταματήσει να υπάρχει; Στην 1η περίπτωση επιστρέφεις object στην δεύτερη pointer.

Επεξ/σία από k33theod
  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)
3 ώρες πριν, k33theod είπε

Αφού το επιστρέφεις και το κάνεις ανάθεση σε άλλο αντικείμενο γιατί να σταματήσει να υπάρχει; Στην 1η περίπτωση επιστρέφεις object στην δεύτερη pointer.

Αυτό σκεφτόμουν και εγώ όταν το έγραφα γιατί ερχόμουν από java. Κολλάω στο εξής:

η μνήμη για αυτό το αντικείμενο δεσμεύτηκε μέσα στη συνάρτηση. Άρα όταν βγω από τη συνάρτηση, δε θα αποδεσμευτεί?

Θα καταλάβαινα να δούλευε αν είχα δεσμεύσει μνήμη ρητά με new/malloc (και μετά έπρεπε να κάνω delete/free) ή να έκανε copy τις τιμές αλλά δε χρησιμοποιεί τον copy constructor.

Έβαλα και ένα -O0 στον g++ για να δω μήπως κάνει καμία περίεργη βελτιστοποίηση αλλά δεν άλλαξε κάτι.

Επεξ/σία από sir ImPeCaBlE
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)
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++ όχι. 

Επεξ/σία από Sacadmerde
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...