zwalnianie pamieci w destruktorze klasy - problem

0

Mam prosta klase do obslugi operacji na duzych liczbach (jest to po prostu implementacja dodawania i mnozenia pisemnego).

Problem jest taki, ze nie wiem jak zwolnic zarezerwowana wczesniej dynamicznie pamiec - i pojawiaja sie spore "wycieki" (przy potegowaniu duzych liczb nawet kilkaset MB).

Podejrzewam, ze nie jest zwalniana pamiec dla jednej zmiennej deklarowanej w definicji operatora przypisania:


void operator = (char *c)         /*przypisanie ciagu znakow*/
       {len = strlen(c);
        wartosc = new int[len];
        for(int i=0;i<len;i++)
            {tmp = c[i];
             wartosc[i] = atoi(&tmp);
            }

       }

Wskaznik

wartosc

jest deklarowany w konstruktorze klasy. Jezeli teraz w destruktorze napisze:

delete[] wartosc

, to caly program mi sie sypie....byc moze dlatego, ze wskaznik jest usuwany, kiedy jest jeszcze potrzebny? Czy moze wcale nie trzeba zwalniac tak zarezerwowanej pamieci i powinienm gdzie indziej szukac przyczyny?

0

Oszczędny jesteś :P Następnym razem daj więcej kodu.

void operator = (char *c)         /*przypisanie ciagu znakow*/
{
  if(wartosc)delete[] wartosc; //<--- to samo w destruktorze
  len = strlen(c);
  wartosc = new int[len];
 
  for(int i=0;i<len;i++)
  {
    tmp = c[i];
    wartosc[i] = atoi(&tmp);
  }
}

PS. wskaźnik

wartosc

wyzeruj w konstruktorze.

0

Niestety, dalej nie dziala :| Program sie sypie:

Abnormal program termination
:| Moze wiecej kodu cos wyjasni:

Konstruktor klasy wyglada teraz tak:

bignum(void)
  {

   *this = "0";
   wartosc = 0;

  }

Druga definicja operatora przypisania:

void operator = (bignum bbb)    /*przypisanie drugiej zmiennej typu bignum*/
     {
      char *c = bbb.ToString();

      if(wartosc)delete[] wartosc;
      len = strlen(c);
      wartosc = new int[len];
      for(int i=0;i<len;i++)
            {tmp = c[i];
             wartosc[i] = atoi(&tmp);
            }


     }

Operator mnozenia:

bignum operator * (int mnoznik)    /*mnozenie przez liczbe calkowita typu int*/
  {bignum bgn;
   bgn.len = lenght()+1;
   bgn.wartosc = Iloczyn1(wartosc,mnoznik,lenght());
   return bgn;
  }

Iloczyn1 to funkcja mnozaca przez liczbe calkowita zwracajaca wynik w postaci tablicy liczb calkowitych.

Teraz mozna juz zdefiniowac mnozenie 2 liczb wielocyfrowych:

bignum operator * (bignum bg)   /*mnozenie przez zmienna typu bignum*/
  {bignum bgn; //wynik

   int n = bg.lenght();
   int m = lenght();
   for(int i=n-1;i>=0;i--)
       {
         bgn = bgn+(*this)*bg.wartosc[i];
         *this = *this*10;
       }

   return bgn;
  }



Definicja operatora dodawania jest dosc dluga, wiec przytocze tylko fragment:

       int *pierwsza;
       int *druga;
       int dl_pierwszej = lenght();
       int dl_drugiej = bg.lenght();

       int *wynik =  new int[dl_pierwszej+1];

       //druga liczba nie moze byc dluzsza od pierwszej
       //jezeli jest dluzsza, zamieniamy liczby miejscami

       if(dl_drugiej<=dl_pierwszej)
          {pierwsza = new int[bg.lenght()];
           druga = new int[lenght()];
           pierwsza = wartosc;  //tablice cyfr typu int zawierajace liczby do dodania
           druga = bg.wartosc;
          }
        else
           {
            druga = new int[bg.lenght()];
            pierwsza = new int[lenght()];
            druga = wartosc;  //tablice cyfr typu int zawierajace liczby do dodania
            pierwsza = bg.wartosc;

            dl_drugiej = lenght();
            dl_pierwszej = bg.lenght();

           }


//..................................dalej dodawanie

Zmienne pierwsza, druga i wynik tez powinienem chyba zwolnic w taki sam sposob?

I jeszcze operator potegowania:

bignum operator ^ (long int wykladnik)  /*potegowanie liczby typu bignum*/
{
 bignum bgn; //wynik

 bgn = *this;

 for(int i =1;i<wykladnik;i++)
     {
      bgn = bgn*(*this);
     }


 return bgn;

Jak widac, wszystko bardzo prosto napisane, ale nie wiem dlaczego nie moge zwolnic pamieci - po zakonczeniu obliczen powinno byc zajete tylko tyle pamieci, ile zajmuje wynik.

0

Pierwszy błąd:

bignum(void)
{
 *this = "0"; //<--- allokacja 1 int'a (patrz operator '=')
  wartosc = 0; //<--- wyzerowanie wskaźnika (wyciek;))
}

Druga sprawa. Użyj referencji przy operatorach:

bignum& operator * (bignum &bg){...}
bignum& operator * (int mnoznik){...}

itd.

PS. dla int'ów i mniejszych typów referencja jest niepotrzebna (no chyba, że wartość parametru ma być modyfikowana do użytku zewnętrznego :P).

Dalej... Allokujesz

pierwsza

i druga

 tyle, że tym wskaźnikom, za chwilę przypisujesz inne adresy (znowu wyciek) ;) 

```cpp
if(dl_drugiej<=dl_pierwszej)
{
 pierwsza = new int[bg.lenght()];
 druga = new int[lenght()];
 pierwsza = wartosc; //<--- pierwsza będzie wskazywać na tablice wartosc
 druga = bg.wartosc; //<--- j/w. tylko dla innego obiektu
}

Na razie tyle. Później się zobaczy ;)

0

Pierwszy błąd:

bignum(void)
{
*this = "0"; //<--- allokacja 1 int'a (patrz operator '=')
wartosc = 0; //<--- wyzerowanie wskaźnika (wyciek;))
}

wyzerowanie wskaznika w konstruktorze - ty to zaproponowales? :-) chyba ze chodzilo o wyzerowanie w destruktorze??

*this = "0" - dlaczego to blad?? Chodzilo mi o to, zeby liczba po zadeklarowaniu miala wartosc 0....

Druga sprawa. Użyj referencji przy operatorach:

bignum& operator * (bignum &bg){...}
bignum& operator * (int mnoznik){...}
itd.

Próbowałem zrobić to tak:

bignum& operator * (bignum bg)   /*mnozenie przez zmienna typu bignum*/
  {bignum *bgn; //wynik

   int n = bg.lenght();
   int m = lenght();
   for(int i=n-1;i>=0;i--)
       {
         *bgn = *bgn+(*this)*bg.wartosc[i];
         *this = *this*10;
       }

   return *bgn;
  }

Ale taka konstrukcja konczy sie konunikatem Acces violation, moze sie zblaznie teraz, ale tylko to wymyslilem :| A moze wynik musze zadeklarowac za pomoca

new

? Ale jak? (jaki rozmiar?)

0

wyzerowanie wskaznika w konstruktorze - ty to zaproponowales? chyba ze chodzilo o wyzerowanie w destruktorze??

Nie, chodziło o wyzerowanie w konstruktorze ;) A błąd jest taki, że pomyliłeś kolejności wykonywania operacji. Tak powinno być:

bignum(void)
{
 wartosc = 0;
 *this = "0"; 
}

Próbowałem zrobić to tak:

bignum& operator * (bignum bg) /mnozenie przez zmienna typu bignum/
{bignum *bgn; //wynik

int n = bg.lenght();
int m = lenght();
for(int i=n-1;i>=0;i--)
{
*bgn = *bgn+(*this)*bg.wartosc[i];
*this = this10;
}

return *bgn;
}

Ale taka konstrukcja konczy sie konunikatem Acces violation, moze sie zblaznie teraz, ale tylko to wymyslilem A moze wynik musze zadeklarowac za pomoca new ? Ale jak? (jaki rozmiar?)

Uuuuuuu mamy poważne braki ;) Dodając referencję do parametrów funkcji nie musisz nic zmieniać w jej implementacji:

bignum operator * (bignum &bg)
{
 bignum bgn; //wynik

 int n = bg.lenght();
 int m = lenght();
 
 for(int i=n-1;i>=0;i--)
 {
   bgn = bgn+(*this)*bg.wartosc[i];
   *this = *this*10;
 }
 return bgn;
}
0

Uuuuuuu mamy poważne braki Dodając referencję do parametrów funkcji nie musisz nic zmieniać w jej implementacji:

Heh no tak, ale ja dodalem referencje nie do parametru, tylko do zwracanej wartosci :) , czyli:

bignum& operator * (bignum bg)

Wtedy to juz raczej trzeba zmienic implementacje?:> Nie moge teraz tego obadac dokladnie bo nie jestem w domu, ale jezeli nic nie zmienie, to bedzie blad typu

Attempt to return reference to....

A tak w ogole, to referencje chyba trzeba dodac i do parametru i do wartosci zwracanej?

0

Heh no tak, ale ja dodalem referencje nie do parametru, tylko do zwracanej wartosci

No tak... tyle, że twój pomysł ma 2 błędy ;)

  1. nie stworzyłeś obiektu bignum (
bignum *bgn;

)
2. jak usuniesz tak stworzony obiekt (zakładając, że

bgn=new bignum

)??? Np. w takim przypadku:

D=A*B*C;

Wtedy to juz raczej trzeba zmienic implementacje?

To co podałem dwa posty wyżej powinno się skompilować bez problemu. Poza dodaniem & do parametru nic się w implementacji metody nie zmieniło ;)

A tak w ogole, to referencje chyba trzeba dodac i do parametru i do wartosci zwracanej?

Nie. W przypadku operatora * tylko do parametru. Podając wcześniej przykład jak używać referencji niepotrzebnie dałem & dla wartości zwracanej :/ Z resztą nie możesz zwrócić referencji do obiektu, którego nie będzie po wyjściu z metody (patrz

bignum bgn;

).

0

No ok, z referencjami zrobilem tak jak mi radzisz, ale mam jeszcze jeden problem: jezeli dodam referencje w parametrze w operatorze +, czyli:

 bignum operator + (bignum &bg)

to w operatorze * nie moge wykorzystac dodawania dwoch liczb bignum:

bignum operator * (bignum &bg)
{
 ......................................

 bgn = bgn+(*this)*bg.wartosc[i]; //tutaj błąd

 .......................................

}

Kompilator podaje mi, że operator + nie jest zdefiniowany dla dwoch danych tego samego typu......:| Jak temu zaradzic?

No i jeszcze nierozwiazany problem zwalniania zmiennej wartosc, jezeli w destruktorze dam

if(wartosc)delete[] wartosc;

, to program mi sie wykrzaczy, nie moge dojsc dlaczego....

[dopisane]

No i ciagle wyglada na to, ze sa gdzies znaczne wycieki, przyklad: obliczam 2^10000. Zajeta pamiec przed obliczaniem: okolo 450 MB, po obliczeniu: 0.99GB. A wynik tyle nie zajmuje... :| Poza tym mimo tego 0.99 GB Menedżer zadan mi pokazuje, ze sam program zajmuje okolo 15 MB pamieci...

0
 bgn = bgn+(*this)*bg.wartosc[i]; //tutaj błąd

A zdefiniowałeś operator * dla

int

'ów??? Chodzi dokładnie o ten motyw: (*this)*bg.wartosc[i];



> No i jeszcze nierozwiazany problem zwalniania zmiennej wartosc, jezeli w destruktorze dam if(wartosc)delete[] wartosc;, to program mi sie wykrzaczy, nie moge dojsc dlaczego....
> 
> [dopisane]
> 
> No i ciagle wyglada na to, ze sa gdzies znaczne wycieki, przyklad: obliczam 2^10000. Zajeta pamiec przed obliczaniem: okolo 450 MB, po obliczeniu: 0.99GB.  A wynik tyle nie zajmuje...  Poza tym mimo tego 0.99 GB Menedżer zadan mi pokazuje, ze sam program zajmuje okolo 15 MB pamieci...

Pamiętaj jedną zasadę: liczba wywołań operatora 
```cpp
new

powinna być równa liczbie wywołań operatora delete

 (lub odwrotnie :P ) Jeżeli obiekt allokuje jakąś tam pamięć to jest odpowiedzialny za jej zwolnienie (na ogół trzymam się tej zasady chociaż czasami bywa tak, że obiekt oddaje taką pamięć innemu obiektowi). Sprawdź czy wszędzie masz tego typu zabezpieczenie (to tylko przykład):

```cpp
if(wartosc)delete[] wartosc; 
wartosc = new int[len];

lub

if(wartosc)
{
 delete[] wartosc; 
 wartosc=NULL;
}

if(len>0)
  wartosc = new int[len];
0

A zdefiniowałeś operator * dla int'ów??? Chodzi dokładnie o ten motyw: (*this)*bg.wartosc[i];

Oczywiscie ze zdefiniowalem. Wyglada teraz tak:

bignum operator * (int &mnoznik)    /*mnozenie przez liczbe calkowita typu int*/
  {bignum bgn;
   bgn.len = lenght()+1;
   bgn.wartosc = Iloczyn1(wartosc,mnoznik,lenght());
   return bgn;
  }

Operator dodawania:

bignum operator + (bignum &bg)

Nie wiem dlaczego kompilator mi daje teraz 2 bledy:

bgn = bgn+(*this)*bg.wartosc[i];  //Błąd: operator '+' nie jest zdefiniowany dla dwoch typow 'bignum' (?)

*this = *this*10; //Błąd j.w : operator '*' nie jest zdefiniowany dla bignum i int

O co chodzi?? Jak nie ma referencji w parametrach to dziala :|

p.s. w poprzednim poscie nie mialem referencji dla int - i byl tylko jeden blad. ( z operatorem +)

0

Oczywiscie ze zdefiniowalem. Wyglada teraz tak:

bignum operator * (int &mnoznik)
...

Zdejmij tą referencje z parametru.

Co do właściwego błędu - spróbuj tak:

bgn = ((*this)*bg.wartosc[i])+bgn;

//EDIT

*this = *this*10;

O co chodzi?? Jak nie ma referencji w parametrach to dziala

Chodzi o to, że w przypadku typu

int

jako parametr możesz podać stałą (nie w znaczeniu const

) - ta 10'ka nie ma swojego adresu w pamięci. Wartość od razu odkładana jest na stosie (o ile metoda operatora nie jest funkcją <code class="cpp">inline

).

0

No pewnie, że nie będzie działać z referencją, bo nie można robić niestałych referencji do wartości tymczasowych. Musisz dać w parametrach const &bignum.

ta 10'ka nie ma swojego adresu w pamięci. Wartość od razu odkładana jest na stosie

To akurat nie ma nic do rzeczy. Referencje i wskaźniki działają również z wartościami na stosie. Poza tym ta wartość prawdopodobnie nie znajdzie się na stosie, a od razu w rejestrze.

0

Musisz dać w parametrach const &bignum.

Dodalem const do parametru operatora +, czyli:

bignum operator + (const bignum &bg)

Dostaje ostrzezenie, ze funkcja non-const lenght() jest wywolywana dla obiektu const, ale błędu nie ma. Czyli lenght() tez powinna zwracac const?

bignum operator * (bignum &bg)   /*mnozenie przez zmienna typu bignum*/
  {bignum bgn; //wynik

   int n = bg.lenght();
   int m = lenght();         //tu ostrzezenie
   for(int i=n-1;i>=0;i--)
       {
         
         bgn = bgn+(*this)*bg.wartosc[i]; 

         *this = *this*10;
       }

   return bgn;
  }

0x666:

bgn = ((*this)*bg.wartosc[i])+bgn;

To tez zadzialalo i nie potrzeba const, ale nie pojmuje jaka to roznica??

0
Krolik napisał(a)

To akurat nie ma nic do rzeczy. Referencje i wskaźniki działają również z wartościami na stosie.

Jak to nie ma nic do rzeczy??? Pamiętaj, że była mowa o parametrach bez

const

, a wtedy referencji do stałych nie możesz dać - potencjalnie możesz chcieć ją zmienić w ciele funkcji/metody.

Oczywiście, co do reszty masz rację ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1