std::string выделять память 2 раза для 1 строки

#c #string #visual-c #allocation

#c #строка #visual-c #выделение

Вопрос:

 #include <iostream>
#include <string>

void* operator new(size_t size) {
    std::cout << "Allocated: " << size << " Bytesn";
    return malloc(size);
}

void operator delete(void* var) {
    std::cout << "Deletedn";
    free(var);
}

int main() {
    std::string name0 = "Ahmed Zaki Marei";
    //std::string name1 = "Lara Mohammed";

    std::cout << name0 << "n";
    //std::cout << name1 << "n";
}
  

Когда я пытаюсь запустить этот код, он выдает мне этот вывод:

 Allocated: 8 Bytes
Allocated: 32 Bytes
Ahmed Zaki Marei
Deleted
Deleted
  

почему сначала выделяется 8 байт, а затем 32 байта?
кто-нибудь может это объяснить, пожалуйста? спасибо! 🙂

Комментарии:

1. @AMZ Попробуйте ввести точку останова operator new , чтобы понять, почему она вызвана.

2. Это не решает вопрос, но не используйте какие-либо функции стандартной библиотеки C внутри вашей собственной operator new и operator delete . Нет гарантии, что operator<< , например, не будет выделено никакой памяти. Использовать функции C нормально, поэтому printf безопасно.

3. sizeof(std::string) это сколько байт выделяется в стеке для хранения объекта string; это совершенно иная цифра, чем количество байт, которые выделяются в куче оператором new- для хранения символов, на которые указывает объект string.

4. @DevSolar есть ли какая-то конкретная причина, по которой вы «не можете посмотреть код библиотеки Microsoft и можете только догадываться»? github.com/microsoft/STL/tree/master/stl

5. operator new Перегрузка вступит в силу для всего, не только для std::string памяти, конечно. Каковы ваши доказательства того, что 2-е выделение предназначено для std::string , а не для чего-то другого, что создается библиотекой C при запуске программы?

Ответ №1:

Как C.M. уже указывал в комментариях: это поведение MS STL в режиме отладки, не связанное с _ITERATOR_DEBUG_LEVEL . В выпуске все в порядке.

Было любопытно, поэтому я протестировал это сам, и стек считывает это при первой точке останова в new :

 operator new(unsigned int size)
std::_Default_allocate_traits::_Allocate(const unsigned int _Bytes)
std::_Allocate<8,std::_Default_allocate_traits,0>(const unsigned int _Bytes)
std::allocator<std::_Container_proxy>::allocate(const unsigned int _Count)
std::_Container_proxy_ptr12<std::allocator<std::_Container_proxy>>::_Container_proxy_ptr12<std::allocator<std::_Container_proxy>>(std::allocator<std::_Container_proxy> amp; _Al_, std::_Container_base12 amp; _Mycont)
std::string::basic_string<char,std::char_traits<char>,std::allocator<char>>(const char * const _Ptr)
main()
  

Давайте посмотрим на basic_string конструктор:

     basic_string(_In_z_ const _Elem* const _Ptr) : _Mypair(_Zero_then_variadic_args_t{}) {
        autoamp;amp; _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal());
        _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2);
  

и что это _Container_proxy_ptr такое на самом деле:

 #if _ITERATOR_DEBUG_LEVEL == 0
#define _GET_PROXY_ALLOCATOR(_Alty, _Al) _Fake_allocator()
template <class _Alloc>
using _Container_proxy_ptr = _Fake_proxy_ptr_impl;
#else // _ITERATOR_DEBUG_LEVEL == 0
#define _GET_PROXY_ALLOCATOR(_Alty, _Al) static_cast<_Rebind_alloc_t<_Alty, _Container_proxy>>(_Al)
template <class _Alloc>
using _Container_proxy_ptr = _Container_proxy_ptr12<_Rebind_alloc_t<_Alloc, _Container_proxy>>;
#endif // _ITERATOR_DEBUG_LEVEL == 0
  

Это _Container_proxy_ptr12 (в конечном итоге вызывается .allocate(1) ) в Debug
и _Fake_proxy_ptr_impl (ничего не делая) в выпуске.