Grałem z nowymi operatorami usuwania i przeciążania, gdy zauważyłem coś dziwnego.Alokacja ciągu w języku C++
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; }
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
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
W rzeczywistości, kiedy zrobić:
int main() { std::string s = "Hello world"; return EXIT_SUCCESS; }
wciąż otrzymuję
Allocating memory...
!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 operatoranew
? Domyślam się, że ma to coś wspólnego z alokacją pamięci dla rzeczywistej literalnej,"Hello world"
... ale powinna to być pamięć statyczna, anew
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.
. UWAGA: 'malloc' /' i 'new' free' /' delete' - są parą –
Dlaczego don używasz debuggera i włamujesz się do swojego 'operatora new', aby zobaczyć kto go nazywa? Strumienie również przydzielają pamięć. –
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