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

Καλεσμα του Destructor μιας καλσης C++


Evgenios1

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

Δημοσ.

Γεια και χαρα. Προσπαθω να φτιαξω ενα τετρις σε 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;
};

 

 

 

 

Δημοσ.

Ευγένιε όταν κάνεις 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#:lol:

 

κάποιος είπε για τη C++:

C++ is for C, what cancer is for lung!

 

Ενω στη C είχες έλεγχο ακριβώς τι έκανες, εδω πρέπει να σκέφτεσε πολύ παραπέρα... αυτό που θές να κάνεις:-)

 

Που να φτάσεις και στα templates/exceptions....:shock:

 

---------- Το μήνυμα προστέθηκε στις 00:51 ----------

 

Το βρήκα, είχε και συνέχεια...

Δημοσ.

Η 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).

Δημοσ.

Αυτό που είχα προτείνει με τον 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)

Αρχειοθετημένο

Αυτό το θέμα έχει αρχειοθετηθεί και είναι κλειστό για περαιτέρω απαντήσεις.

  • Δημιουργία νέου...