2011-12-06 17 views
5

Wiem, że destruktor mapy wywołuje wszystkie destruktory zawartych elementów. Co się dzieje nastl map <char*,char*> destructor

map<char*,char*> ? 

nie mogę zobaczyć, gdzie ten kod jest w /usr/include/c++/4.4


EDIT: Powinienem powiedział

map<const char*, const char*, ltstr> 

jak w

http://www.sgi.com/tech/stl/Map.html

+4

Zauważ, że 'mapa' indeksowana przez' char * 'prawdopodobnie nie zrobi tego, co chcesz. Dopasuje się do adresu ciągu, a nie do treści ciągu. –

+0

W GCC 4.6 musisz zajrzeć do '/ usr/include/C++/4.6/bits/stl_map.h' , a na 4.6 zajrzyj do'/usr/include/C++/4.4/bits/stl_map.h' –

Odpowiedz

6

Co dzieje

Nic. Jeśli dynamicznie alokujesz pamięć, wycieknie - nie ma automatycznego destruktora dla char*.

Zamiast tego należy użyć klasy std::string lub podobnej.

+1

. "nic" jest prawdopodobnie trochę mylące. Cała pamięć, której wewnętrznie przydzielono mapę dla swojego drzewa, zostaje ostatecznie usunięta. – sbi

+2

Myślę, że technicznie ma destruktor, który niczego nie robi. (Możesz wywołać 'A-> ~ char *()') Ale efekt jest taki sam. –

14

Po zniszczeniu elementu map<char*,char*> wszystkie elementy są w nim zawarte. Destruktor każdego elementu jest wywoływany, jeśli jest typu klasy.

Pamiętaj jednak, co dokładnie zawiera powyższa mapa. To nie jest łańcuch, jak można się spodziewać - to tylko wskaźniki do ciągów. Same struny nie są niszczone. Tylko wskaźniki. delete nigdy nie jest wywoływany na wskaźnikach.

Sprawa w punkcie:

map<char*, char*> strings; 

char* key = new char[10]; 
char* value = new char[256]; 
/* ... */ 
strings.insert(key,value); 

W powyższym od delete nigdy nie wezwała wskaźników utworzonych przez wywołań new, ta pamięć będzie przeciekać kiedy strings wykracza poza zakres.

To jest dobra ilustracja, dlaczego powinieneś unikać używania surowych wskaźników, new i delete. W twoim przypadku prawdopodobnie lepszym wyborem byłby wariant map<string,string>.

EDIT:

Jak @sbi wspomniano w komentarzach, kolejny powód, dlaczego chcesz map<string,string> nad map<char*,char*> dlatego ze map<string,string> Klucze są porównywane przez wartość, a nie-wskaźnik wartości.

Rozważmy:

#include <map> 
#include <iostream> 
#include <string> 
using namespace std; 

int main() 
{ 
    static const char MyKey[] = "foo"; 
    const char bar[] = "bar"; 
    typedef map<const char*,const char*> Strings; 
    Strings strings; 
    strings.insert(make_pair(MyKey,bar)); 

    string ss = "foo"; 
    Strings::const_iterator it = strings.find(ss.c_str()); 
    if(it == strings.end()) 
     cout << "Not Found!"; 
    else 
     cout << "Found"; 

} 

Zasadniczo masz wkładając element z kluczem "foo", a następnie szukając tego elementu. Przetestuj powyższy kod, a przekonasz się, że go nie znaleziono. Jeśli jednak spróbować tego:

#include <map> 
#include <iostream> 
#include <string> 
using namespace std; 

int main() 
{ 
    typedef map<string,string> Strings; 
    Strings strings; 
    strings.insert(make_pair("foo","bar")); 

    string ss = "foo"; 
    Strings::iterator it = strings.find(ss); 
    if(it == strings.end()) 
     cout << "Not Found~!"; 
    else 
     cout << "Found"; 
} 

... masz zachowanie ty naprawdę chciał.

+2

Doskonała odpowiedź, '+ 1' ode mnie. Innym powodem preferowania 'std :: string' jest to, że w' std :: map 'klucz będzie porównywany przez _address_, a nie przez _content_. Rzadko tego chcesz. – sbi

+0

@sbi: Dobra uwaga, zredagowana. –

+0

+1 bardzo dobra odpowiedź –

1

Zgadzam się, że std::map<string, string> ma zalety w stosunku do std::map<char*, char*>.Specjalnie posiadanie klucza jako wartości zamiast wskaźnika zapewni oczekiwane wyniki wyszukiwania/wyszukiwania.

Ale czasami potrzebujemy wskaźnika w definicji mapy, szczególnie w części wartości, gdy część wartości mapy jest ciężkim obiektem klasy zdefiniowanej przez użytkownika. Przez ciężki przedmiot rozumiem, że konstruktor kopiowania klasy wykonuje znaczną część pracy. W takich scenariuszach wartość części mapy powinna być wskaźnikiem. Używanie surowego wskaźnika spowodowałoby wyciek pamięci, jak wspomniano powyżej. Inteligentny wskaźnik byłby lepszym wyborem, zapewniając brak wycieków pamięci.

Przykładowy przykład: Rozważmy poniższy klasa

class ABCD 
{ 
public: 
    ABCD(const int ab) : a(ab) 
    {cout << "Constructing ABC object with value : " << a << endl;} 

    ~ABCD() 
    {cout << "Destructing ABC object with value : "<< a << endl;} 

    void Print() 
    { cout << "Value is : " << a << endl;} 

private: 
    int a; 
}; 

Rozważmy kod gdzie w inteligentny wskaźnik powyższej klasy stosuje się:

{ 
    std::map<_tstring, std::shared_ptr<ABCD>> myMap; 
    _tstring key(_T("Key1")); 
    myMap.insert(std::make_pair(key, std::make_shared<ABCD>(10))); 
    auto itr = myMap.find(key); 
    itr->second->Print(); 

    myMap[key] = std::make_shared<ABCD>(20); 
    itr = myMap.find(key); 
    itr->second->Print(); 
} // myMap object is destroyed, which also calls the destructor of ABCD class 

Wyjście z wyżej kod jest:

Constructing ABC object with value : 10 

Value is : 10 

Constructing ABC object with value : 20 

Destructing ABC object with value : 10 

Value is : 20 

Destructing ABC object with value : 20