Evgenios1 Δημοσ. 30 Οκτωβρίου 2009 Δημοσ. 30 Οκτωβρίου 2009 Γεια και χαρα. Προσπαθω να φτιαξω ενα τετρις σε console win32. Το προβλημα ειναι περιεργο (για μενα ειναι), εχω μια κλαση στη stack απο την οποια καλω καποιες συναρτησεις. >[color="#0000ff"]#include[/color] [color="#a31515"]"stdafx.h"[/color] [color="#8000ff"]int[/color] _tmain([color="#8000ff"]int[/color] argc, _TCHAR* argv[]) { Screen s; Graphic g; g.LoadObjectFile([color="#a31515"]"E:\\L.obj"[/color]); [color="#0000ff"][b]while[/b][/color]([color="#0000ff"][b]true[/b][/color]) { s.AddGraphic(g); Sleep([color="#ff8000"]200[/color]); s.RemoveGraphic(g); g.MoveDown(); } getchar(); [color="#0000ff"][b]return[/b][/color] [color="#ff8000"]0[/color]; } Αυτο που δεν καταλαβενω ειναι γιατι οταν καλω την "AddGraphic" μετα απο αυτη καλειται και ο destructor της κλασης. Ο κωδικας απο τις κλασεις graphic.cpp >[color="#0000ff"]#include[/color] [color="#a31515"]"StdAfx.h"[/color] [color="#0000ff"]#include[/color] [color="#a31515"]"Graphic.h"[/color] Graphic::Graphic([color="#0000ff"][b]void[/b][/color]) { count = [color="#ff8000"]0[/color]; idx = [color="#ff8000"]0[/color]; } Graphic::~Graphic([color="#0000ff"][b]void[/b][/color]) { [color="#008000"][i]//delete[] pView;[/i][/color] } [color="#8000ff"]bool[/color] Graphic::Next(View *p) { [color="#0000ff"][b]if[/b][/color](idx<count) { *p = pView[idx]; idx++; [color="#0000ff"][b]return[/b][/color] [color="#0000ff"][b]true[/b][/color]; } [color="#0000ff"][b]else[/b][/color] { idx=[color="#ff8000"]0[/color]; [color="#0000ff"][b]return[/b][/color] [color="#0000ff"][b]false[/b][/color]; } } [color="#0000ff"][b]void[/b][/color] Graphic::LoadObjectFile([color="#8000ff"]char[/color] *path) { FILE *f = fopen(path,[color="#a31515"]"r"[/color]); fread(&count,[color="#0000ff"][b]sizeof[/b][/color]([color="#8000ff"]int[/color]),[color="#ff8000"]1[/color],f); pView = [color="#0000ff"][b]new[/b][/color] View[count]; fread(pView,[color="#0000ff"][b]sizeof[/b][/color](View),count,f); fclose(f); } [color="#0000ff"][b]void[/b][/color] Graphic::MoveDown([color="#0000ff"][b]void[/b][/color]) { [color="#0000ff"][b]for[/b][/color]([color="#8000ff"]int[/color] i = [color="#ff8000"]0[/color] ; i < count ; i ++ ) pView[i].Y++; } [color="#0000ff"][b]void[/b][/color] Graphic::MoveLeft() { [color="#0000ff"][b]for[/b][/color]([color="#8000ff"]int[/color] i = [color="#ff8000"]0[/color] ; i < count ; i ++ ) pView[i].X--; } [color="#0000ff"][b]void[/b][/color] Graphic::MoveRight() { [color="#0000ff"][b]for[/b][/color]([color="#8000ff"]int[/color] i = [color="#ff8000"]0[/color] ; i < count ; i ++ ) pView[i].X++; } [color="#0000ff"][b]void[/b][/color] Graphic::PerLeft() { [color="#008000"][i]/* int temp; for(int i = 0 ; i < count ; i ++ ) { temp = pView[i].X; pView[i].X = pView[i].Y; pView[i].Y = temp; }*/[/i][/color] } [color="#0000ff"][b]void[/b][/color] Graphic::PerRight() { } graphic.h #pragma once class Graphic { private: View *pView; int count; int idx; public: Graphic(void); ~Graphic(void); bool Next(View *p); void LoadObjectFile(char* path); void MoveDown(void); void MoveLeft(void); void MoveRight(void); void PerRight(void); void PerLeft(void); }; screen.cpp >[color="#0000ff"]#include[/color] [color="#a31515"]"StdAfx.h"[/color] [color="#0000ff"]#include[/color] [color="#a31515"]"Screen.h"[/color] Screen::Screen([color="#0000ff"][b]void[/b][/color]) { hCmd = GetStdHandle(STD_OUTPUT_HANDLE); } Screen::~Screen([color="#0000ff"][b]void[/b][/color]) { } [color="#0000ff"][b]void[/b][/color] Screen::SetCharAt(_TCHAR tchar,[color="#8000ff"]int[/color] x,[color="#8000ff"]int[/color] y) { COORD c={x,y}; SetConsoleCursorPosition(Screen::hCmd,c); WriteConsole(hCmd,&tchar,[color="#ff8000"]1[/color],&cWrited,NULL); c.X = [color="#ff8000"]0[/color]; c.Y = [color="#ff8000"]0[/color]; SetConsoleCursorPosition(hCmd,c); } [color="#0000ff"][b]void[/b][/color] Screen::AddGraphic(Graphic graphic) { View v; [color="#0000ff"][b]while[/b][/color](graphic.Next(&v)) SetCharAt(v.Data,v.X,v.Y); } [color="#0000ff"][b]void[/b][/color] Screen::RemoveGraphic(Graphic graphic) { View v; [color="#0000ff"][b]while[/b][/color](graphic.Next(&v)) SetCharAt(' ',v.X,v.Y); } screen.h #pragma once class Screen { private: HANDLE hCmd; ULONG cWrited; void SetCharAt(_TCHAR,int,int); public: Screen(void); ~Screen(void); void AddGraphic(Graphic graphic); void RemoveGraphic(Graphic graphic); }; view > #pragma once struct View { unsigned int X:8,Y:8,Data:16; };
bxenos Δημοσ. 31 Οκτωβρίου 2009 Δημοσ. 31 Οκτωβρίου 2009 Ευγένιε όταν κάνεις AddGraphic με αυτή τη συνάρτηση: >void Screen::AddGraphic(Graphic graphic) αναγκάζεις τη C++ να περάσει ένα αντίγραφο της παραμέτρου (δεν περνάς με αναφορά την παράμετρο) στην συνάρτηση. Αρα όταν τελειώνει η συνάρτηση, καλεί τον destructor στο αντίγραφο της graphic. Εσύ έχεις βέβαια μέσα στη Graphic instance δείκτες με αντικείνα. Ο αντιγραφέας απο το πρωτότυπο αντικείμενο στο αντίγραφο (copy constructor) δεν ξέρει πως να τα αντιγράψει και αντιγράφει μόνο τις διευθύνσεις τους. Αρα έχεις δυο αντικείμενα με ίδιο περιεχόμενο. Αυτό συμβαίνει διότι ΔΕΝ έχεις φτιάξει δικό σου copy construtcor και η C++ φτιάχνει έναν απλό δικό της που απλά αντιγράφει τιμές. Φυσικά ο destructor του αντιγράφου καταστρέφει και τα περιεχόμενα και το πρωτο αντικείμενο που έμεινε πίσω έχει σαβούρες. Πως θα το κάνεις: Ένας τρόπος είναι με μετρητές (o destructor σβήνει αντικείμενα όταν ο μετρητής είναι 0) π.χ. > class foo { int *array; int *referenses_count; foo(){ array = new ... referenses_count = new int; *referenses_count = 1; } ~foo(){ if(--(*referenses_count) == 0){ delete [] array; delete referenses_count; } } foo(foo& f){//copy constructor array = f.array; referenses_count = f.referenses_count; (*referenses_count)++; } } ή > void Screen::AddGraphic(Graphic *graphic){... το αντικείμενο graphic θα το βάζεις σε μια λίστα στον ιδιοκτητη του (αν αυτός είναι ο Screen, θα την έχεις εκεί), και αυτό το αντικείμενο θα είναι υπευθυνο να τη σβήσει/destruct όταν δεν θα χρειάζεται άλλο } .... π.χ. s.AddGraphic(new Graphic()); ... Αλλο ένα παράδειγμα απο μορφή κώδικα που με βολεύει και χρησιμοποιώ: > class mgdiobject {};//interface class mfont : public mgdiobject {} class mcurve : public mbdiobject {} ... class mdisplay { λίστα με mgdiobject fonts; λίστα με mgdiobject curnes; .... }//βασικό interface με virtual functions και linked list αντικειμένων class mprinter:public mdisplay {}//εκτυπωτης class mscreen : public mdisplay {}//video area ... mprinter dc(...);//ας φτιάξουμε ένα display για εκτυπωση dc.fonts.push(new mfont("arial",12,...)); dc.fonts.push(new mfont("helvetica",9,...)); dc.write(point(0,0),"hello world");//τυπωσε με helvetica delete dc.fonts.pop();//destruct font object helvetica dc.write(point(10,10),"good bye");//τυπωσε με arial delete dc.fonts.pop();//destruct font object arial αν κατα λάθος ξεχασω αντικείμενα που δεν τα σβήσω εγώ, θα σβηστουν αυτόματα απο τους destructor των λιστων στο mgdiobject και θα έχω και μήνυμα στο debug terminal ---------- Το μήνυμα προστέθηκε στις 00:39 ---------- κάτι τέτοια κάνει η C++ και πήγε ο κόσμος στη C# κάποιος είπε για τη C++: C++ is for C, what cancer is for lung! Ενω στη C είχες έλεγχο ακριβώς τι έκανες, εδω πρέπει να σκέφτεσε πολύ παραπέρα... αυτό που θές να κάνεις Που να φτάσεις και στα templates/exceptions.... ---------- Το μήνυμα προστέθηκε στις 00:51 ---------- Το βρήκα, είχε και συνέχεια...
Evgenios1 Δημοσ. 31 Οκτωβρίου 2009 Μέλος Δημοσ. 31 Οκτωβρίου 2009 Βασικα δεν εχω ακουστα το "copy constructor". Παω να ψαξω .
kagelos Δημοσ. 31 Οκτωβρίου 2009 Δημοσ. 31 Οκτωβρίου 2009 Η C++ μπορεί και περνάει αντικείμενα by value,όπως περνάς έναν ακέραιο. Δηλ. αν καλέσεις void foo(int n) { n++; } int a = 5; foo(a); //το a παραμένει 5, είναι by value η κλήση. Έτσι κάνει και με τα αντικείμενα η C++ (αν δεν περάσεις reference ή pointer) Οπότε αν είχαμε SomeClass obj; foo (obj); void foo(SomeClass o) { o.set(45); } Δεν αλλάζει το obj. Καλείται ο default copy contructor και περνάει στην foo ένα αντίγραφο του obj. Το οποίο καταστρέφεται αφού τελειώσει η συνάρτηση (άρα καλείται ο destructor).
bxenos Δημοσ. 1 Νοεμβρίου 2009 Δημοσ. 1 Νοεμβρίου 2009 Αυτό που είχα προτείνει με τον counter, θα μπορουσε να τυποποιηθεί και με χρήση αντικειμένου: > #include <stdio.h> class InstanceCounter { unsigned *pcounter; public: InstanceCounter() { pcounter = new unsigned; *pcounter = 1; } ~InstanceCounter() { if(--(*pcounter) == 0){ delete pcounter; printf("counter deletion\n"); } else printf("counter reduction\n"); } InstanceCounter(InstanceCounter& c){ printf("copy constructor called\n"); pcounter = c.pcounter; (*pcounter)++; } unsigned Counter(void) const { return *pcounter; } }; class foo { int *xxx; InstanceCounter cnt; public: foo(){ xxx = new int[326]; } ~foo(){ if(cnt.Counter() == 1){ printf("destruct with delete mem\n"); delete []xxx; } else {//else:κάποιος άλλος χρησιμοποιεί ακομα τον πίνακα μας printf("destruct without delete mem\n"); } } }; /* o copy constructor θα δημιουργηθεί αυτόματα και θα καλεί τους copy constructor των μελών της class foo. Η cnt έχει copy constructor και θα κληθει, η xxx έχει copy constructor και απλά αντιγράφει την τιμή της. οπότε */ [b]void[/b] main(){ foo a; { foo b = a;//εδω έκανα αντιγραφη (copy construct) και θα γινει destruct αμεσως μετα } //εδω θα γίνει destruct to a } και τα αποτελέσματα: > copy constructor called (δημιουργια destruct without delete mem (καταστροφη counter reduction (καταστροφη b.cnt) destruct with delete mem (καταστροφή a) counter deletion (καταστροφη a.cnt)
Προτεινόμενες αναρτήσεις
Αρχειοθετημένο
Αυτό το θέμα έχει αρχειοθετηθεί και είναι κλειστό για περαιτέρω απαντήσεις.