2013-07-23 27 views
6

Grałem z nowymi operatorami usuwania i przeciążania, gdy zauważyłem coś dziwnego.Alokacja ciągu w języku C++

  1. mam:

    void* operator new(size_t size) 
    { 
        std::cout << "Allocating memory..." << std::endl; 
    
        void* p = malloc(size); 
    
        if (NULL == p) 
        { 
         throw std::bad_alloc(); 
        } 
    
        return p; 
    } 
    
  2. Kiedy zrobić:

    int main() 
    { 
        int* x = new int(1); 
        std::cout << *x << std::endl; 
        delete x; 
        return EXIT_SUCCESS; 
    } 
    

    Wszystko działa zgodnie z oczekiwaniami i uzyskać:

    Allocating memory... 
    1 
    
  3. Ale kiedy zrobić:

    int main() 
    { 
        std::string* s = new std::string("Hello world"); 
        std::cout << *s << std::endl; 
        delete s; 
        return EXIT_SUCCESS; 
    } 
    

    uzyskać:

    Allocating memory... 
    Allocating memory... 
    Hello world 
    
  4. W rzeczywistości, kiedy zrobić:

    int main() 
    { 
        std::string s = "Hello world"; 
        return EXIT_SUCCESS; 
    } 
    

    wciąż otrzymuję Allocating memory...!

  5. Wreszcie robię:

    int main() 
    { 
        std::string s = "Hello world"; 
        std::cout << &s << std::endl; 
        while (true); 
    } 
    

    Aby uzyskać coś takiego:

    $ ./test & 
    [1] 8979 
    Allocating memory... 
    0xbfc39a68 
    $ cat /proc/8979/maps | grep stack 
    bfc27000-bfc3c000 ... [stack] 
    

    więc teraz jestem pewien, że zmienna s jest przydzielona na stosie ... ale potem, co dzwoniąc do operatora new? Domyślam się, że ma to coś wspólnego z alokacją pamięci dla rzeczywistej literalnej, "Hello world" ... ale powinna to być pamięć statyczna, a new to pamięć dynamiczna.

Co się dzieje?

Aktualizacja

Po przeczytaniu komentarzy i debugowania przykład ja chciałem do wniosku, że rzeczywiście, gdy konstruktor ciąg nazywa, to przydziela pamięć na stercie dla jego wewnętrznej realizacji. Można to zaobserwować śledząc wezwanie new:

(gdb) b 13 // that's the std::cout << "Allocating memory..." << std::endl; line 
(gdb) r 
... Breakpoing 1, operator new (size=16) at test.cpp:13 ... 
(gdb) backtrace 
#0 operator new (size=16) at main.cpp:13 
#1 std::string::_Rep::_S_create(unsigned int, unsigned int, std::allocator<char> const&)() from /usr/lib/libstdc++.so.6 
... 

i czytania std :: string (dobrze, basic_string.tcc) kod źródłowy:

template<typename _CharT, typename _Traits, typename _Alloc> 
typename basic_string<_CharT, _Traits, _Alloc>::_Rep* 
basic_string<_CharT, _Traits, _Alloc>::_Rep:: 
_S_create(size_type __capacity, size_type __old_capacity, 
      const _Alloc& __alloc) 
{ 

    ... 

    void* __place = _Raw_bytes_alloc(__alloc).allocate(__size); 
    _Rep *__p = new (__place) _Rep; // Bingo! 
    __p->_M_capacity = __capacity; 

    ... 

} 

Więc tak. Programowanie jest fajne.

+0

. UWAGA: 'malloc' /' i 'new' free' /' delete' - są parą –

+1

Dlaczego don używasz debuggera i włamujesz się do swojego 'operatora new', aby zobaczyć kto go nazywa? Strumienie również przydzielają pamięć. –

+2

Cały punkt 'std :: string' polega na tym, że opakowuje' char * 'i jest właścicielem/zarządza nim dla ciebie. Więc kiedy tworzysz 'std :: string', przydziela trochę pamięci do przechowywania rzeczywistych znaków. Konstruktor może nawet przyjąć niestandardowy przydział. – BoBTFish

Odpowiedz

5

Kiedy piszesz

std::string s = "Hello world"; 

jesteś wywołanie konstruktora string (const char* s);, specyfikacja ten konstruktor jest Copies the null-terminated character sequence (C-string) pointed by s. Tak więc konstruktor przydziela pamięć do przechowywania kopii.

+0

* Ty * w ostatnim zdaniu jest mylące, lepiej byłoby sformułowane * Więc 's' należy przydzielić pamięci do przechowywania kopii *, ale wtedy radziłbym zmienić nazwę parametru konstruktora, ponieważ jest to również 's' . –

+0

Czy teraz jest lepiej? – hivert

+0

Wygląda lepiej (przynajmniej dla mnie), dziękuję :) –

1
std::string s = "Hello world"; 

prawej stronie jest pamięci statycznej s konstruktor alokuje nową pamięć i kopie "Hello World",

0

std :: string jest strukturą, która hermetyzuje łańcuch w stylu c.

Pierwsza alokacja dotyczy struktury kontenerów ciągów.
Drugi przydział dotyczy kopii napisu, który jest tworzony w konstrukcji ciągu znaków.

0

Sam obiekt przydziela miejsce dla rzeczywistych znaków za pomocą new. Odbywa się to w konstruktorze ciągu.

3

Przykład 3 .: (pierwsza uwaga, że ​​trzeba ust std::string *s zamiast std::string s, ponieważ new zwraca wskaźnik).

Korzystanie gdb ustawić punkt przerwania na linii std :: cout i backtraced dwa połączenia:

Pierwszy z nich to jest new napisałeś w swoim kodzie. Druga dzieje się nazywać od wewnątrz libstdC++ so.6 (w moim przypadku):

(gdb) backtrace 
#0 operator new (size=36) at test.cpp:6 
#1 0x00007ffff7b78a89 in std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&)() from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 
#2 0x00007ffff7b7a495 in char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag)() from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 
#3 0x00007ffff7b7a5e3 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)() from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 
#4 0x0000000000400da1 in main() at test.cpp:20 
+0

+1 za nie tylko udzielenie odpowiedzi, ale także podpowiedź, jak zbadać sprawę. –