2016-10-26 11 views
1

В libc++ я обнаружил, что basic_string destructor не вызывается, как только строка выходит из области памяти, освобождается память, вызывая удаление вместо того, чтобы вызывать его destructor, а затем вызывая оператор delete от destructor, почему так?Возможная ошибка в libC++ для mac os, string destructor не вызывается, когда строка obj выходит за пределы области видимости

Может кто-нибудь объяснить это? см Образец программы

void * operator new (size_t len) throw (std::bad_alloc) 
{ 
    void * mem = malloc(len); 
    if ((mem == 0) && (len != 0)) 
     throw std::bad_alloc(); 
    return mem; 
} 

void operator delete (void * ptr) throw() 
{ 
    if (ptr != 0) 
     free(ptr); 
} 

int main(int argc, const char * argv[]) 
{ 
    std::string mystr("testing very very very big string for string class"); 
    std::string mystr2(mystr1.begin(),mystr.end()); 
} 

пункт Put перерыв на новые и удалять, а затем проверить стек вызовов. новый оператор получает вызов из класса basic_string, в то время как удаление получает вызванное от конца main, в то время как в идеале basic_string destructor должен был вызвать сначала, а затем оператор delete должен быть вызван через deallocate вызов allocator, это справедливо для создания второй строки.

+0

Вы включили оптимизацию? Почему вы считаете это идеальным, что деструктор называется? Поскольку единственным наблюдаемым эффектом деструктора является вызов для удаления, это допускается по правилу as-if. – user2079303

+0

Я не включил оптимизацию, я знаю, что для basic_string он вызовет оператор new из ассемблерного кода, но в случае создания второй строки он использует распределитель и вызывает новый оператор через метод allocate. Я также вижу деструктор в классе basic_string, и, посмотрев код, я чувствую, что его написанный для вызова в момент уничтожения для случая, когда строка была создана через распределитель. –

ответ

2

Я вижу то же самое в отладчике, что и вы; Я не знаю наверняка, но я подозреваю, что материал становится инкрустированным. Деструктор для basic_string очень мал; один тест (для оптимизации небольших строк), а затем вызов функции распределителя deallocate (через allocate_traits). std::allocator s функция распределения также довольно мала, просто обертка вокруг operator delete.

Вы можете проверить это, написав собственный распределитель. (Позже: см. Ниже)

Больше материала, которое я создал при расследовании этого вопроса; прочитайте, если вы заинтересованы.

[Примечание: есть ошибка в вашем коде - во второй строке Вы писали: mystr1.begin(),mystr.end()) - где mystr1 объявлен?]

Если предположить, что это опечатка, я попробовал некоторые немного другой код:

#include <string> 
#include <new> 
#include <iostream> 

int news = 0; 
int dels = 0; 

void * operator new (size_t len) throw (std::bad_alloc) 
{ 
    void * mem = malloc(len); 
    if ((mem == 0) && (len != 0)) 
     throw std::bad_alloc(); 
    ++news; 
    return mem; 
} 

void operator delete (void * ptr) throw() 
{ 
    ++dels; 
    if (ptr != 0) 
     free(ptr); 
} 

int main(int argc, const char * argv[]) 
{ 
    { 
    std::string mystr("testing very very very big string for string class"); 
    std::string mystr2(mystr.begin(),mystr.end()); 
    std::cout << "News = " << news << "; Dels = " << dels << std::endl; 
    } 
    std::cout << "News = " << news << "; Dels = " << dels << std::endl; 

} 

Если запустить этот код, он печатает (по крайней мере для меня):

News = 2; Dels = 0 
News = 2; Dels = 2 

, что и должно быть.

Если я брошу код в compiler explorer, то я вижу оба звонка на basic_string::~basic_string(), точно так, как я ожидаю. (Ну, я вижу три из них, но один из них находится в блоке обработки исключений, который заканчивается вызовом _Unwind_resume).

Позже - этот код:

#include <string> 
#include <new> 
#include <iostream> 

int news = 0; 
int dels = 0; 

template <class T> 
class MyAllocator 
{ 
public: 
    typedef T value_type; 

    MyAllocator() noexcept {} 

    template <class U> 
    MyAllocator(MyAllocator<U>) noexcept {} 

    T* allocate(std::size_t n) 
    { 
     ++news; 
     return static_cast<T*>(::operator new(n*sizeof(T))); 
    } 

    void deallocate(T* p, std::size_t) 
    { 
     ++dels; 
     return ::operator delete(static_cast<void*>(p)); 
    } 

    friend bool operator==(MyAllocator, MyAllocator) {return true;} 
    friend bool operator!=(MyAllocator, MyAllocator) {return false;} 
}; 


int main(int argc, const char * argv[]) 
{ 
    { 
    typedef std::basic_string<char, std::char_traits<char>, MyAllocator<char>> S; 
    S mystr("testing very very very big string for string class"); 
    S mystr2(mystr.begin(),mystr.end()); 
    std::cout << "Allocator News = " << news << "; Allocator Dels = " << dels << std::endl; 
    } 
std::cout << "Allocator News = " << news << "; Allocator Dels = " << dels << std::endl; 
} 

печатает:

Allocator News = 2; Allocator Dels = 0 
Allocator News = 2; Allocator Dels = 2 

, который подтверждает, что аллокатор вызывался.

+0

Я согласен с вашими выводами, и это доказывает вашу точку зрения. –

+0

Но как только вы добавите ниже строки в код и поместите точку останова на определение удаления оператора, вы увидите трассировку вызова, как ожидалось. str2.get_allocator(). Deallocate (const_cast (str2.data()) str2.length()); Поведение вызова destructor и явный вызов deallocate отличается, я обнаружил это при отладке чего-то, где мое приложение использует DLL, и когда DLL вызывает метод stringstream.str(), в этом случае новый оператор DLL вызывается, пока оператор delete на уровне приложения вызывается, когда мы явно вызываем deallocate, добавляя вышеприведенную строку, а затем удаляем оператор вызываемой DLL. –

+0

В случае libstdC++ новый и удаленный оператор на уровне приложения получил вызов. –

 Смежные вопросы

  • Нет связанных вопросов^_^