eraskino Δημοσ. 8 Ιανουαρίου 2013 Δημοσ. 8 Ιανουαρίου 2013 Καλησπέρα.Γράφω αντικείμενα child σε αρχείο μέσα από δείκτη της parent. Τα ανακτώ με τον ίδιο τρόπο και καλώ την display.Το πρόβλημα είναι όταν κάνω display (όταν διαβάζω το αντικείμενο από το αρχείο και κάνω display) το char* name το βγάζει σκουπίδια μνήμης ή σκάει ενώ το age το βγάζει κανονικά.Έχω φάει ώρες αλλά δεν μπορώ να καταλάβω. Τι κάνω λάθος;Thanks. class parent { char * name; int age; parent( char* n, int a) { name = new char [strlen(n) + 1]; strcpy(name, n); age = a; } virtual size_t getSize() = 0; virtual void display() { cout << name << age; } }; class child : public parent { public: child(char* n, int a) : parent(n, a) {} size_t getSize() { return sizeof(child); } void display() { parent::display(); } }; void store_package() { char* name = new char [20]; cin.getline(send, 20); int age; cin >> age; // γράφω στο αρχείο αντικείμενο child parent *p = new child(name, age); ofstream file("temp.bin", ios::binary | ios::app ); size_t tempSize; tempSize = p->getSize(); file.write( (char*) &tempSize,sizeof(size_t)); file.write( (char*) p,tempSize); file.close(); } void show_packages() { // ανακτώ το αντικείμενο child ifstream file("temp.bin", ios::binary); size_t tempSize=0; file.read( (char*) &tempSize, sizeof(size_t)); parent* p = (child*) malloc(tempSize); file.read( (char*) p,tempSize); p->display(); file.close(); }
defacer Δημοσ. 8 Ιανουαρίου 2013 Δημοσ. 8 Ιανουαρίου 2013 Κάνεις λάθος πολλά πράγματα, αλλά το συγκεκριμένο στο οποίο αναφέρεσαι είναι πως γράφεις στο αρχείο τα στεγνά περιεχόμενα της μνήμης που φιλοξενεί ένα child (βασικά parent) object. Τα περιεχόμενα αυτά περιέχουν ένα char* ο οποίος δείχνει σε μνήμη που έχει κάποια τιμή (το όνομα) όταν το αντικείμενο δημιουργείται. Όταν φορτώνεις το αντικείμενο αργότερα (σε επόμενο τρέξιμο του προγράμματος), η αυτός ο char* δείχνει στην ίδια διεύθυνση μνήμης που έδειχνε ο αρχικός char* όταν έκανες save. Προφανώς εφόσον μιλάμε για διαφορετικό process, κατά την ανάκτηση η διεύθυνση στην οποία δείχνει ο pointer αυτός μπορεί να περιέχει οτιδήποτε, και συνήθως θα περιέχει σκουπίδια. Αυτό που γίνεται είναι βασικά undefined behavior, οπότε οποιοδήποτε αποτέλεσμα είναι τεχνικά αναμενόμενο. Αυτό που θα έπρεπε να κάνεις είναι πρώτον να το γυρίσεις σε "κανονική C++", π.χ. η parent να είναι class parent { std::string name; int age; public: parent(char* n, int a) : name(n), age(a) {} virtual ~parent() {} virtual void display(ostream& os) const { os << name << age; } }; και απο κει και πέρα να επιλέξεις πώς προτιμάς να γίνεται το serialization. Για πολλούς και διάφορους λόγους δεν είναι δυνατόν να το κάνεις με ξερό γράψιμο της μνήμης όπως είναι τώρα. Μια καλή επιλογή θα ήταν να κάνεις αρχικά ένα pure virtual function με την οποία κάθε derived class ξέρει πώς να γράψει τον εαυτό της, και στη συνέχεια να το συνδυάσεις με ένα free operator << ο οποίος θα σου επιτρέψει να κάνεις το serialization πολυμορφικά σε οποιοδήποτε stream θέλεις. Παράδειγμα εδώ.
eraskino Δημοσ. 8 Ιανουαρίου 2013 Μέλος Δημοσ. 8 Ιανουαρίου 2013 Ευχαριστώ φίλε! Καταρχήν να πώ ότι η άσκηση που έχω είναι τελείως διαφορετική από αυτήν απλά έγραψα ένα παράδειγμα για το πρόβλημα με το char*. Επίσης το char* δεν μπορώ να το κάνω string γιατί είναι προϋπόθεση της άσκησης. Αυτό που λες στην αρχή το έχω καταλάβει το θέμα είναι πώς θα λύσω το πρόβλημα αυτό. Ευχαριστώ για το link!
defacer Δημοσ. 8 Ιανουαρίου 2013 Δημοσ. 8 Ιανουαρίου 2013 Η αλήθεια είναι πως το char* vs string δεν αλλάζει ιδιαίτερα το τοπίο. Αυτό που πρέπει να κάνεις είναι να βάλεις κάθε class να κάνει serialize τον εαυτό της. Θα πρέπει να υπάρχει μια function η οποία θα γράφει ένα ένα τα πεδία του αντικειμένου σε ένα stream (γιατί c++ άρα streams) και τυπικά μια ακόμα static function η οποία θα διαβάζει τις τιμές των πεδίων από ένα stream, θα δημιουργεί ένα αντικείμενο αυτής της class και θα το επιστρέφει. Γελοίο παράδειγμα: class person { int age; public: person(int age) : age(age) {} void serialize(std::ostream& os) { os << age; } static person* deserialize(std::istream& is) { int age; is >> age; return new person(age); } }; Το παραπάνω κατά κάποιο τρόπο δουλεύει αλλά έχει πάρα πολλά προβλήματα: Τι θα κάνει η is >> age αν το stream έχει κι άλλα ψηφία που ακολουθούν αυτά της πραγματικής ηλικίας; (είναι πολύ εύκολο να γίνει, κάνε serialize 2 objects στο ίδιο std::stringstream και μετά δοκίμασε να κάνεις deserialize το πρώτο)Άρα χρειάζεται κάποιος τρόπος να διαχωρίζεις τα "δικά σου" δεδομένα από τυχόν άλλα που βρίσκονται στο stream πριν ή μετά από αυτά. Εντελώς ανάλογα, τι θα γινόταν αν το object είχε 2 int πεδία, ή 2 string πεδία;Άρα χρειάζεται τρόπος να διαχωρίζεις τα πεδία μεταξύ τους, και πρέπει να το κάνεις manually. Ειδικά για το string σκέψου πως εφόσον μπορεί τα περιεχόμενά του να είναι οποιαδήποτε, δεν έχεις άλλο τρόπο χειρισμού παρά να αποθηκεύσεις στο stream το μήκος του string πριν τα περιεχόμενά του, έτσι ώστε να ξέρεις πόσο να διαβάσεις στη συνέχεια. Αλλά και το μήκος πρέπει να αποθηκευτεί με ένα τρόπο τέτοιο ώστε το δικό του μήκος να είναι υπολογίσιμο. Σαν πρώτη προσέγγιση μπορείς απλά να αποθηκεύεις πάντα ακριβώς 4 bytes, αν και βέβαια υπάρχουν και πιο εξελιγμένες. Πώς μπορείς να κάνεις (de)serialize αντικείμενα διαφορετικών classes στο ίδιο stream?Χρειάζεται κάθε αντικείμενο να γράφει στο stream κάτι που να μας επιτρέπει να αναγνωρίζουμε την ταυτότητά του, και μετά κάποιος τρόπος να αντιστοιχίζουμε αυτό το κάτι με την κλήση μιας κατάλληλης function που δημιουργεί αντικείμενα αυτού του τύπου (factory method pattern). Ελπίζω να πήρες μια ιδέα γιατί είναι πάρα πολλά και δε με παίρνει να τα καλύψω όλα. 1
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα