Wyciek pamięci w poracie CB Umieszczanie plików tekstowych w

dział ogólny

Wyciek pamięci w poracie CB Umieszczanie plików tekstowych w

Nowy postprzez Darek_C++ » środa, 23 czerwca 2010, 16:13

Mam pytanie tyczące porady Twojej porady CB z której chciałem skorzystać w swoim programie
"Umieszczanie plików tekstowych w zasobach programu"
W kodzie aż dwa razy tworzymy obiekt po przez new :
Kod: Zaznacz cały
std::auto_ptr<TMemoryStream>stream(new TMemoryStream);
//oraz
TMemoryStream *Ms = new TMemoryStream;

// I samo wywołanie:

Memo1->Lines->LoadFromStream(LoadTxtFromResource(ID_TXT1));


Ale nigdzie w kodzie nie mamy wywołania delete dla utworzonego w ten sposób obiektu. Nie powoduje to błędu związanego z "wyciekiem pamięci " ?
----
W książęce C++ Builder 2006 podobny przykład gdzie wywołanie był mniej więcej tak kodowany:
Kod: Zaznacz cały
TMemoryStream *Mstr = LoadTxtFromResource(ID_TXT1);
Memo2->Lines->LoadFromStream(Mstr);
delete Mstr;

ALe i tak zostaje jedno new bez delete
Znawców tematu proszę o ustosunkowanie się do tego zagadnienia.

Pozdrawiam
Avatar użytkownika
Darek_C++
Elektrowied
Elektrowied
 
Posty: 454
Dołączył(a): piątek, 25 lipca 2008, 14:33
Podziękował : 66
Otrzymał podziękowań: 4
System operacyjny: Windows XP Pro SP2
Kompilator: Turbo Explorer C++
Gadu Gadu: 0
    Windows XPFirefox

Re: Wyciek pamięci w poracie CB Umieszczanie plików tekstowych w

Nowy postprzez banita » środa, 23 czerwca 2010, 17:20

std::auto_ptr<TMemoryStream>stream(new TMemoryStream);

ta pamiec zostanie zwolniolna samodzielnie gdy 'stream' wyjdzie poza swoj zakres waznosci. 'stream' zwiolni ja w swoim destruktorze.

TMemoryStream *Ms = new TMemoryStream;

ta pamiec nie jest zwalniana(chyba ze dzieje sie to w innym fragmencie kodu) a powinna byc.
Avatar użytkownika
banita
Kreacjusz
Kreacjusz
 
Posty: 283
Dołączył(a): poniedziałek, 28 lipca 2008, 20:07
Podziękował : 1
Otrzymał podziękowań: 18
System operacyjny: Windows 7 Professional
Kompilator: C++Builder 2010 Update 5,
Delphi 2010 Update 5,
NetBeans 6.9(MinGw + Qt),
Visual Studio 2008 + Qt
Gadu Gadu: 0
    Windows 7Opera

Re: Wyciek pamięci w poracie CB Umieszczanie plików tekstowych w

Nowy postprzez Cyfrowy Baron » środa, 23 czerwca 2010, 18:10

Jest oczywiste, że jeżeli tworzysz jakiś obiekt to powinieneś dodać kod, który po tym obiekcie posprząta. Wspomnianą przez Ciebie funkcję można by napisać trochę inaczej i użyć delete do usunięcia obiektu Ms. Nie będzie jednak wycieku pamięci, gdyż obiekt Ms jest obiektem lokalnym, tworzonym wewnątrz funkcji i poza funkcją nie istnieje. Brak delete jednak sprawia, że w pamięci pozostają śmieci po obiekcie. Najlepiej byłoby tutaj użyć do tworzenia obiektu Ms wzorca klasy auto_ptr, gdyż ten zawsze po sobie sam posprząta.



Teraz zrobiłbym to inaczej:

Tworzysz w notatniku plik *.RC np. zasoby.rc o przykładowej treści:


ID_TXT_1 RCDATA "pliki tekstowe\plik1.txt"
ID_TXT_2 RCDATA "pliki tekstowe\plik2.txt"



Włączasz go do projektu poprzez menu: Project -> Add to project...

A potem funkcja i wywołanie:

Kod: Zaznacz cały
#include <memory>

String __fastcall LoadTxtFromResource(char *res)
{
 HRSRC rsrc = FindResource(HInstance, res, RT_RCDATA);
 if(!rsrc)
 {
  Application->MessageBox(
  L"Nie można przeprowadzić operacji. Taki zasób nie istnieje!",
  L"BŁĄD!", MB_OK | MB_ICONSTOP);
  return "";
 }

 DWORD Size = SizeofResource(HInstance, rsrc);
 HGLOBAL MemoryHandle = LoadResource(HInstance, rsrc);

 if(MemoryHandle == NULL) return 0;

 BYTE *MemPtr = (BYTE *)LockResource(MemoryHandle);

 String Result;

 TEncoding *Encoding;
 std::auto_ptr<TStringStream> sStream(new TStringStream(NULL, Encoding->UTF8, true));
 sStream->Write(MemPtr, Size);
 sStream->Position = 0;
 Result = sStream->ReadString(Size);

 delete MemPtr;

 return Result;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Memo1->Text = LoadTxtFromResource("ID_TXT_1");
}
//---------------------------------------------------------------------------
  
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: Wyciek pamięci w poracie CB Umieszczanie plików tekstowych w

Nowy postprzez Darek_C++ » środa, 23 czerwca 2010, 18:41

OK tylko, że pierwotna funkcja z porady CB tak wyszukuje w zasobach:
Kod: Zaznacz cały
TMemoryStream *LoadTxtFromResource(unsigned short ID)
{
   HRSRC rsrc = FindResource(HInstance, MAKEINTRESOURCE(ID), RT_RCDATA);
   if(!rsrc)
   {
      Application->MessageBox("Nie można przeprowadzić operacji. Taki zasób nie istnieje!", "BŁĄD!", MB_OK | MB_ICONSTOP);
      return 0;
   }
//// dalszy kod....

/// I jej wywolanie
//Memo2->Lines->LoadFromStream(LoadTxtFromResource(ID_TXT1));

I ta działa, teraz druga odwołująca się do tego samego zasobu co pierwsza działająca zwraca błąd o nie istnieniu zasobu:
Kod: Zaznacz cały
String __fastcall LoadDaneTxtFromResource(char *res)
{
   HRSRC rsrc = FindResource(HInstance, res, RT_RCDATA);
   if(!rsrc)
   {
      Application->MessageBox("Nie można przeprowadzić operacji. Taki zasób nie istnieje!", "BŁĄD!", MB_OK | MB_ICONSTOP);
      return 0;
   }
/// dalsza czesc funkcji

// jej wywołanie wywala blad z MessageBox'a
Memo2->Text = LoadDaneTxtFromResource("ID_TXT1");

Dlaczego tak się dzieje ...
Avatar użytkownika
Darek_C++
Elektrowied
Elektrowied
 
Posty: 454
Dołączył(a): piątek, 25 lipca 2008, 14:33
Podziękował : 66
Otrzymał podziękowań: 4
System operacyjny: Windows XP Pro SP2
Kompilator: Turbo Explorer C++
Gadu Gadu: 0
    Windows XPFirefox

Re: Wyciek pamięci w poracie CB Umieszczanie plików tekstowych w

Nowy postprzez Cyfrowy Baron » czwartek, 24 czerwca 2010, 08:37

Obie funkcje działają. Nie umieściłbym niesprawdzonej funkcji. To Ty coś pomieszałeś. Być może używasz drugiej funkcji do pliku zasobów stworzonego z pierwszej porady, a przecież podałem jak należy stworzyć plik zasobów dla tego przykładu.
Zamiast włączania skompilowanego pliku zasobów *.RES podałem, że należy stworzyć plik *.RC i włączyć go do projektu poprzez menu Project -> Add to project. Podczas kompilacji programu plik *.RC zostanie skompilowany do postaci *.RES i włączony do projektu o ile używasz nowszych wersji środowiska C++Builder. Jeżeli używasz starszych wersji to musisz skompilować plik *.RC ręcznie.

Plik zasobów ma następującą konstrukcję:

nazwa_zasobu typ_zasobu lokalizacja_pliku_włączanego_w_zasoby


ID_TXT_1 RCDATA "plik1.txt"
ID_TXT_2 RCDATA "plik2.txt"



W pierwszej funkcji odwołujesz się po identyfikatorze zasobu, w drugiej funkcji identyfikator nie jest definiowany, więc funkcja szuka po nazwie zasobu. Definiowanie identyfikatora jest zbędne, poza tym zawsze można go zdefiniować w pliku źródłowym aplikacji.

Błąd wyskoczy tylko w jednej sytuacji, gdy zasób o podanej nazwie zasobu nie zostanie odnaleziony, czyli w zasadzie tylko w sytuacji, gdy podasz nieprawidłową nazwę zasobu. Plik *.RC nie zostanie skompilowany do postaci *.RES gdy plik włączany w zasoby nie zostanie odnaleziony w podanej lokalizacji, ale to oczywiście sprawia, że projekt wogóle się nie skompiluje.
Takoż więc, prawdopodobnie podajesz nieprawidłową nazwę zasobu. Funkcja działa poprawnie, to Ty popełniasz gdzieś błąd.




Funkcję wciąż można nieco uprościć:

Kod: Zaznacz cały
#include <memory>

String __fastcall LoadTxtFromResource(wchar_t *res)
{
 HRSRC rsrc = FindResource(HInstance, res, RT_RCDATA);
 if(!rsrc)
 {
      Application->MessageBox(
      L"Nie można przeprowadzić operacji. Taki zasób nie istnieje!",
      L"BŁĄD!", MB_OK | MB_ICONSTOP);
      return "";
 }

 DWORD Size = SizeofResource(HInstance, rsrc);
 HGLOBAL MemoryHandle = LoadResource(HInstance, rsrc);

 if(MemoryHandle == NULL)
 {
  ShowMessage("Błąd");
  return "";
 }

 BYTE *MemPtr = (BYTE *)LockResource(MemoryHandle);

 String Result;
 std::auto_ptr<TStringStream> sStream(new TStringStream("", TEncoding::UTF8, false));
 sStream->Write(MemPtr, Size);
// sStream->Position = 0;
 Result = sStream->DataString;

 delete MemPtr;

 return Result;
}
 


Jednak przy pobieraniu pliku tekstowego kodowanego np. w UTF8 z zasobów do obiektu Memo pojawią się artefakty, gdyż Memo zmienia kodowanie, ale tylko przy wczytywaniu pliku z dysku lub przy wczytywaniu z pamięci, dlatego dobrze by było przerobić funkcje tak, by zwracała typ TStream:

Kod: Zaznacz cały
#incldue <memory>

//---------------------------------------------------------------------------
TStringStream *__fastcall LoadTxtFromResource(wchar_t *res)
{
 HRSRC rsrc = FindResource(HInstance, res, RT_RCDATA);
 if(!rsrc)
 {
      Application->MessageBox(
      L"Nie można przeprowadzić operacji. Taki zasób nie istnieje!",
      L"BŁĄD!", MB_OK | MB_ICONSTOP);
      return NULL;
 }

 DWORD Size = SizeofResource(HInstance, rsrc);
 HGLOBAL MemoryHandle = LoadResource(HInstance, rsrc);

 if(MemoryHandle == NULL)
 {
  ShowMessage("Błąd");
  return NULL;
 }

 BYTE *MemPtr = (BYTE *)LockResource(MemoryHandle);

 String Result;
 std::auto_ptr<TStringStream> sStream(new TStringStream("", TEncoding::UTF8, false));
 sStream->Write(MemPtr, Size);
 sStream->Position = 0;
 //Result = sStream->DataString;

 delete MemPtr;

 return sStream.release();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{

 Memo1->Lines->LoadFromStream(LoadTxtFromResource(L"ID_TXT_1"), TEncoding::UTF8);
}
//---------------------------------------------------------------------------
 


Przy takim zwracaniu danych nie ma jednak potrzeby określania kodowania, gdyż obiekty automatycznie rozpoznają kodowanie i odpowiednio się ustawią, dlatego ustawianie kodowania można sobie darować:

Kod: Zaznacz cały
#include <memory>

TStringStream *__fastcall LoadTxtFromResource(wchar_t *res)
{
 HRSRC rsrc = FindResource(HInstance, res, RT_RCDATA);
 if(!rsrc)
 {
      Application->MessageBox(
      L"Nie można przeprowadzić operacji. Taki zasób nie istnieje!",
      L"BŁĄD!", MB_OK | MB_ICONSTOP);
      return NULL;
 }

 DWORD Size = SizeofResource(HInstance, rsrc);
 HGLOBAL MemoryHandle = LoadResource(HInstance, rsrc);

 if(MemoryHandle == NULL)
 {
  ShowMessage("Błąd");
  return NULL;
 }

 BYTE *MemPtr = (BYTE *)LockResource(MemoryHandle);

 String Result;
 std::auto_ptr<TStringStream> sStream(new TStringStream());
 sStream->Write(MemPtr, Size);
 sStream->Position = 0;
 //Result = sStream->DataString;

 delete MemPtr;

 return sStream.release();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Memo1->Lines->LoadFromStream(LoadTxtFromResource(L"ID_TXT_1"));
}
//---------------------------------------------------------------------------
 





txr.rar




:!: ::. Uzupełnij profil bym wiedział jakiej wersji środowiska używasz. .:: :!:
Nie masz wystarczających uprawnień, aby zobaczyć pliki załączone do tego postu.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: Wyciek pamięci w poracie CB Umieszczanie plików tekstowych w

Nowy postprzez Darek_C++ » czwartek, 24 czerwca 2010, 08:55

Z całym szacunkiem, ale wydaje mi się nic nie pokręciłem zresztą widać to w fragmentach zamieszczonego kodu nie ma w nim błędu dlatego tym bardziej mnie dziwi, że zwracany jest błąd o braku zasobu który jest, bo wykrywa go pierwszy kod.
---
Za pozostałe przykłady bardzo dziękuję.

PS ja używam Turbo Explorer C++ .
Avatar użytkownika
Darek_C++
Elektrowied
Elektrowied
 
Posty: 454
Dołączył(a): piątek, 25 lipca 2008, 14:33
Podziękował : 66
Otrzymał podziękowań: 4
System operacyjny: Windows XP Pro SP2
Kompilator: Turbo Explorer C++
Gadu Gadu: 0
    Windows XPFirefox

Re: Wyciek pamięci w poracie CB Umieszczanie plików tekstowych w

Nowy postprzez Cyfrowy Baron » czwartek, 24 czerwca 2010, 09:00

Jak widzisz w poście załączyłem działający program wraz z kodem źródłowym więc nie pisz mi, że coś nie działa, skoro sam możesz przekonać się, że jednak działa. Skoro mój kod działa, a Ty nie popełniłeś błędu to co???

Załącz kod źródłowy przykładowego programu, wtedy pokaże Tobie gdzie popełniasz błąd, gdyż bazując na moim kodzie nie mogę, ponieważ działa w 100%.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: Wyciek pamięci w poracie CB Umieszczanie plików tekstowych w

Nowy postprzez Darek_C++ » czwartek, 24 czerwca 2010, 09:28

OK, już rozumiem swój błąd - nie idzie tak jak robiłem stosować jednocześnie sposobu, ze starej porady z dołączaniem pliki :
#include "Zasoby.rh"
z ostatnio podanym sposobem z samym dodaniem do zasobu pliki *.rc "Włączamy do projektu za pomocą menu Project | Add to Project... plik RC"

czyli tak jak w kodzie:
Kod: Zaznacz cały
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

#ifndef ZASOBY_RH
#define ZASOBY_RH
#define ID_TXT_1 1000
#endif
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
   : TForm(Owner)
{
}
//---------------------------------------------------------------------------
String __fastcall LoadTxtFromResource(char *res)
{
   HRSRC rsrc = FindResource(HInstance, res, RT_RCDATA);
   if(!rsrc)
   {
      Application->MessageBox("Nie można przeprowadzić operacji. Taki zasób nie istnieje!", "BŁĄD!", MB_OK | MB_ICONSTOP);
      return 0;
   }
   else
   {
      ShowMessage("Zasoby OK");
   }
}

TMemoryStream *LoadTxtFromResourceX(unsigned short ID)
{
   HRSRC rsrc = FindResource(HInstance, MAKEINTRESOURCE(ID), RT_RCDATA);
   if(!rsrc)
   {
        // Ten zasob nie zostanie wykryty
        Application->MessageBox("Nie można przeprowadzić operacji. Taki zasób nie istnieje!", "BŁĄD!", MB_OK | MB_ICONSTOP);
      return 0;
   }
   else
   {
      ShowMessage("Zasoby OK");
   }
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
   Memo1->Text = LoadTxtFromResource("ID_TXT_1");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
   Memo1->Lines->LoadFromStream(LoadTxtFromResourceX(ID_TXT_1));
}
//---------------------------------------------------------------------------


Pozdrawiam
---
Cały czas miałem w profilu info o [Kompilator: Turbo Explorer C++]
Avatar użytkownika
Darek_C++
Elektrowied
Elektrowied
 
Posty: 454
Dołączył(a): piątek, 25 lipca 2008, 14:33
Podziękował : 66
Otrzymał podziękowań: 4
System operacyjny: Windows XP Pro SP2
Kompilator: Turbo Explorer C++
Gadu Gadu: 0
    Windows XPFirefox

Re: Wyciek pamięci w poracie CB Umieszczanie plików tekstowych w

Nowy postprzez Cyfrowy Baron » czwartek, 24 czerwca 2010, 09:29

Czyli nie czytałeś dokładnie, a przecież pisałem właśnie o tym błędzie w przedostatnim poście. :oops:



Darek_C++ napisał(a):Cały czas miałem w profilu info o [Kompilator: Turbo Explorer C++]


Nie miałeś! Kilka minut temu zaktualizowałem Twój profil.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: Wyciek pamięci w poracie CB Umieszczanie plików tekstowych w

Nowy postprzez polymorphism » czwartek, 24 czerwca 2010, 09:38

Tak nawiasem:
Kod: Zaznacz cały
BYTE *MemPtr = (BYTE *)LockResource(MemoryHandle);

(...)

delete MemPtr;   //<--- !!!   

Jeśli te LockResource to funkcja WinAPI, to to delete jest ewidentnym błędem. Tym wyrażeniem/operatorem możesz usuwać tylko pamięć przydzieloną przez new.

Z PSDK:

    The pointer returned by LockResource is valid until the module containing the resource is unloaded. It is not necessary to unlock resources because the system automatically deletes them when the process that created them terminates.

C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: Wyciek pamięci w poracie CB Umieszczanie plików tekstowych w

Nowy postprzez Cyfrowy Baron » czwartek, 24 czerwca 2010, 10:41

Masz rację. Trochę się zagalopowałem, szczególnie, że w jednym z kodów tworzę TEncoder *Encode i nie usuwam tego. Nie zastanawiając się nad tym zasugerowałem się tym co napisał Darek_c++ w pierwszym poście, że dwa obiekty nie są usuwane przez delete, pierwszy to był Ms i rzeczywiście powinien być usunięty, ale to wymagało zmiany konstrukcji funkcji, drugi sugerowany to był właśnie MemPtr.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox


  • Podobne tematy
    Odpowiedzi
    Wyświetlone
    Ostatni post

Powrót do Ogólne problemy z programowaniem

Kto przegląda forum

Użytkownicy przeglądający ten dział: Brak zalogowanych użytkowników i 8 gości

cron