Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[c++] problemy ze zwalnianiem pamieci (memcpy, free, delete)

Ostatnio zmodyfikowano 2013-10-22 19:57
Autor Wiadomość
b00rt00s
Temat założony przez niniejszego użytkownika
[c++] problemy ze zwalnianiem pamieci (memcpy, free, delete)
» 2013-10-22 06:14:54
Od razu podam, ze słaby jestem w te klocki, więc proszę o cierpliwość. Chcę napisać, który program otrzyma dane w postaci wypełnionego obszaru pamięci o wskaźniku char* i znanym rozmiarze. Wiadomo, że obszar zajmowany jest całkowicie przez obiekt znanej klasy. Teraz muszę przerobić ten obszar pamięci na normalny obiekt klasy. Żeby sobie przetrenować takie operacje, napisałem prosty program testowy:
C/C++
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
   
    //zrodlo danych
    string tekst1( "jakis tekst" );
   
    char * zrodlo =( char * )( & tekst1 ); //wspomniany obszar pamięci
    size_t rozmiar = tekst1.length();
   
    //cel danych
    char * bufor = new char[ rozmiar ];
    string * tekst2 = new( bufor ) string();
    memcpy( tekst2, zrodlo, rozmiar );
   
   
    //wypisanie danych - poprawnie
    cout <<* tekst2 << endl;
   
   
    //uwalnianie pamieci - poprawnie
    free( tekst2 );
   
    //uwalnianie pamieci - crash aplikacji z błędem: "double free or corruption (fasttop): 0x0000000001e78010" itd.
    //delete tekst2;
   
    return 0;
}

Moje pytanie brzmi: dlaczego wykonanie operatora delete kończy się crashem? Albo inaczej, jak uzyskać poprawny wskaźnik na typ string, którym można manipulować jak każdym innym wskaźnikiem?

P.S. Docelowo docelowa klasa ma być zupełnie inna, jednak próbuje zrozumieć mechanizm.
P-94272
Mrovqa
» 2013-10-22 07:31:36
1. Jeżeli coś alokujesz operatorem new to możesz to dealokować tylko i wyłącznie operatorem delete. Podobnie - dla malloc wykorzystuje się free.
2. Ty wiesz co to jest std::string? To jest klasa, a nie tablica znaków. W ten sposób nie pobierzesz wskaźnika char*. Masz dwie możliwości:
C/C++
string * zrodlo = & tekst1;
char * zrodlo2 = & tekst1[ 0 ]; // to cie prawdopodobnie interesuje
P-94274
b00rt00s
Temat założony przez niniejszego użytkownika
» 2013-10-22 09:33:40
Chyba źle zostałem zrozumiany. W tym przypadku użyłem std::string jako przykładu. Docelowo chodzi mi o to, że mogę dostawać obiekty w postaci obszaru pamięci. Ten obszar może być zapisany jako tablica typu char. Teraz mi chodzi o to, jak ten obszar przekonwertować na wskaźnik na obiekt klasy. Klasa obiektu jest oczywiście określona.

[EDIT]

Cholera, wiem co zrobiłem nie tak... Wywołując memcpy wykonałem kopię stringa, ale bez głębokiego kopiowania, a wewnątrz stringa jest przecież wskaźnik (tablica) typu char. Wykonując operator delete wykonuję destruktor klasy, który usuwa tą tablicę. Sęk w tym, że ona będzie usunięta automatycznie, bo pierwszy string był na stosie i mam podwójne zwolnienie pamięci...  Używając free nie wywołuję destruktora i dlatego nie ma błędu...
P-94275
pekfos
» 2013-10-22 09:43:22
length() nie zwraca rozmiaru obiektu stringa. Poza tym nie można mieszać malloc() i free() z new i delete.

Wykonując operator delete
Nie można wywoływać delete, dla obiektów utworzonych przez placement new.

PS: Prawdę mówiąc, w tym kodzie nie ma niczego poprawnego, jeśli chodzi o obsługę dynamicznej pamięci. Poczytaj najpierw, jak się tego używa i nie mówię tylko o placement new.
P-94277
b00rt00s
Temat założony przez niniejszego użytkownika
» 2013-10-22 19:57:41
PS: Prawdę mówiąc, w tym kodzie nie ma niczego poprawnego, jeśli chodzi o obsługę dynamicznej pamięci.
Widzę, że cały czas nie zostałem zrozumiany.
Linie:
C/C++
char * zrodlo =( char * )( & tekst1 ); //wspomniany obszar pamięci
size_t rozmiar = tekst1.length();
to coś na co nie mam wpływu! Tak właśnie dostanę obiekt: jako wypełniony obszar pamięci. Teraz moim zadaniem jest przekształcić ten obszar na wskaźnik do konkretnej klasy.

Poeksperymentowałem trochę i doszedłem do rozwiązania problemu:

C/C++
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    //zrodlo danych
    string tekst1( "jakis tekst" );
    char * zrodlo =( char * )( & tekst1 ); //wspomniany obszar pamięci
   
    //cel danych
    string * tekst2 =( string * ) zrodlo; //rzutowanie wskaznika
    string * tekst3 = new string( * tekst2 ); //wykonanie poprawnej kopii z wykorzystaniem konstruktora kopiujacego zamiast 'mempcy'
   
   
    //wypisanie danych - poprawnie
    cout <<* tekst3 << endl;
   
   
    //uwalnianie pamieci - poprawnie
    delete tekst3;
   
    return 0;
}

Mój problem polegał na tym, że należało poprawnie skopiować obiekt z obszaru pamięci do nowej zmiennej. Zamiast memcpy wykonałem rzutowanie wskaźnika, a następnie wykorzystałem konstruktor kopiujący, który wykonał głębokie kopiowanie. Po przeanalizowaniu kodu valigrindem  okazało się, że nie było żądnych wycieków pamięci. Temat chyba do zamknięcia...
P-94337
« 1 »
  Strona 1 z 1