2008-09-16 24 views
10

Say mam następujące C++:usuwanie bufora za pomocą innego rodzaju wskaźnika?

char *p = new char[cb]; 
SOME_STRUCT *pSS = (SOME_STRUCT *) p; 
delete pSS; 

Czy to bezpieczne według standardu C++? Czy muszę odesłać z powrotem do char*, a następnie użyć delete[]? Wiem, że będzie działać w większości kompilatorów C++, ponieważ jest to zwykły zwykły-danych, bez destruktorów. Czy gwarantuje się bezpieczeństwo?

Odpowiedz

6

Nie, to niezdefiniowane zachowanie - kompilator mógł wiarygodnie zrobić coś innego, a jak mówi wejście C++ FAQ że thudbang związana, operator delete[] może być przeciążony zrobić coś innego do operator delete. Czasami można się z tym uporać, ale dobrą praktyką jest też nawyk dopasowywania delete [] z new [] w przypadkach, w których nie możesz.

0

Chociaż powinny pracy, nie sądzę, można zagwarantować, że jest bezpieczny, ponieważ SOME_STRUCT nie jest char * (chyba że jest to jedynie typedef).

Dodatkowo, ponieważ używasz różnych typów odniesień, jeśli nadal używasz dostępu * p, a pamięć została usunięta, pojawi się błąd środowiska wykonawczego.

2

C++ podstawowa [5.3.5.2] stwierdza:

operandzie ma typ klasy, argument jest przekształcany do rodzaju wskaźnika wywołując wspomniane powyżej konwersji funkcję, a przekształcony operand użyte zamiast oryginalnego operandu dla pozostałej części tej sekcji. W przypadku alternatywy wartość argumentu delete może być zerową wartością wskaźnika. Jeśli nie jest to zerowa wartość wskaźnika, w pierwszym wariancie (usuń obiekt), wartość argumentu usunięcia będzie wskaźnikiem do obiektu nie będącego tablicą lub wskaźnikiem do podobiektu (1.8) reprezentującego podstawę klasa takiego obiektu (punkt 10). Jeśli nie, zachowanie jest niezdefiniowane. W drugim alternatywnym (tablica kasowania) wartość argumentu delete jest wartością wskaźnika, która wynika z poprzedniej tablicy new-expression.77) Jeśli nie, to zachowanie jest niezdefiniowane. [Uwaga: oznacza to, że składnia wyrażenia delete musi być zgodna z typem obiektu przydzielonego przez new, a nie z składnią nowego wyrażenia. -end note] [Uwaga: wskaźnik do typu const może być operandem wyrażenia delete; nie jest konieczne odrzucenie stałej (5.2.11) wyrażenia wskaźnika wskaźnika, zanim zostanie użyte jako operand wyrażenia delete. -end note]

4

Bardzo w to wątpię.

Istnieje wiele wątpliwych sposobów zwalniania pamięci, na przykład można użyć delete w swojej macierzy char (zamiast delete[]) i prawdopodobnie będzie dobrze działać. I blogged szczegółowo o tym (przeprosiny za samo-łącze, ale jest to łatwiejsze niż przepisanie wszystkiego).

Kompilator to nie tyle problem, co platforma. Większość bibliotek będzie używać metod alokacji bazowego systemu operacyjnego, co oznacza, że ​​ten sam kod może zachowywać się inaczej w systemie Mac w porównaniu do systemu Windows w porównaniu do systemu Linux. Widziałem przykłady tego i każdy z nich był wątpliwym kodem.

Najbezpieczniej jest zawsze przydzielać i zwalniać pamięć przy użyciu tego samego typu danych. Jeśli przydzielanie char s i zwracanie ich do innego kodu, może być lepiej zapewniając specyficzne przeznaczyć/metody deallocate:

SOME_STRUCT* Allocate() 
{ 
    size_t cb; // Initialised to something 
    return (SOME_STRUCT*)(new char[cb]); 
} 

 

void Free(SOME_STRUCT* obj) 
{ 
    delete[] (char*)obj; 
} 

(Przeciążenie operatorów new i delete może być również opcja, ale nigdy nie lubiłem tego.)

0

To zadziała poprawnie, jeśli pamięć zostanie wskazana na i wskazywany przez ciebie wskaźnik to POD. W takim przypadku żaden destruktor nie zostanie wywołany, a alokator pamięci nie będzie wiedział lub nie będzie dbał o typ przechowywany w pamięci.

Jedynym przypadkiem, w którym jest to poprawne w przypadku typów innych niż POD, jest sytuacja, w której podnośnik jest podtypem wskaźnika (np. Wskazuje się na samochód z pojazdem *), a destruktor wskaźnika został uznany za wirtualny.

0

To nie jest bezpieczne i żadna z dotychczasowych odpowiedzi nie podkreśliła wystarczająco szaleństwa. Po prostu nie rób tego, jeśli uważasz się za prawdziwego programistę lub kiedykolwiek chcesz pracować jako profesjonalny programista w zespole. Możesz tylko powiedzieć, że twoja struktura zawiera nie destruktora w tej chwili, jednak układasz nieprzyjemny prawdopodobnie kompilator i pułapkę specyficzną dla systemu na przyszłość. Ponadto Twój kod prawdopodobnie nie zadziała zgodnie z oczekiwaniami. Najlepsze, na co możesz liczyć, to że się nie psuje. Jednak podejrzewam, że powoli dostaniesz wyciek pamięci, alokacji tablicy przez nowe bardzo często alokować dodatkową pamięć w bajtach przed do zwróconego wskaźnika. Nie uwolnisz pamięci, o której myślisz, że jesteś. Dobra procedura przydzielania pamięci powinna wykryć to niedopasowanie, podobnie jak narzędzia takie jak Lint itp.

Po prostu nie rób tego i oczyść się z umysłu, niezależnie od sposobu myślenia doprowadziło cię nawet do rozważenia takich bzdur.

0

Zmieniono kod, aby korzystać z malloc/free. Chociaż wiem, jak MSVC implementuje nowe/usuwać dla zwykłych starych danych (a SOME_STRUCT w tym przypadku było strukturą Win32, tak proste C), chciałem tylko wiedzieć, czy była to przenośna technika.

Nie, więc użyję czegoś, co jest.

0

Jeśli używasz malloc/free zamiast nowego/delete, malloc i free nie będą dbać o typ.

Tak więc, jeśli używasz podobnego do COD POD (zwykłe stare dane, takie jak typ wbudowany lub struct), możesz usunąć malloc i zwolnić inny. Zwróć uwagę, że jest to kiepski styl, nawet jeśli działa.

2

Jest to bardzo podobne pytanie do jednego, że ja tu odpowiedzi: link text

w skrócie, nie, to nie jest bezpieczne według standardu C++.Jeśli z jakiegoś powodu potrzebujesz obiektu SOME_STRUCT przydzielonego w obszarze pamięci, który ma różnicę w wielkości od size_of(SOME_STRUCT) (i lepiej byłoby być większym!), Lepiej jest użyć funkcji przydzielania surowców, takiej jak globalna operator new, aby wykonać przydzielanie i tworzenie instancji obiektu w pamięci pierwotnej z miejscem docelowym new. Miejsce docelowe new będzie bardzo tanie, jeśli typ obiektu nie ma konstruktora.

void* p = ::operator new(cb); 
SOME_STRUCT* pSS = new (p) SOME_STRUCT; 

// ... 

delete pSS; 

To będzie działać przez większość czasu. Powinno zawsze działać, jeśli SOME_STRUCT jest POD-struct. Będzie również działać w innych przypadkach, jeśli konstruktor SOME_STRUCT nie wyrzuci, a jeśli SOME_STRUCT nie ma niestandardowego operatora, usuń. Ta technika eliminuje również potrzebę wykonywania rzutów.

::operator new i ::operator delete są najbliżej odpowiednik C++ 's do malloc i free i jako takie (w przypadku braku zadajnikami klasa) nazywane są odpowiednio przez new i delete wyrażeń mogą (ostrożnie!) Być używane w połączeniu.

Powiązane problemy