2016-10-26 3 views
1

libc++私はbasic_string destructorが呼び出されないことを発見しました。演算子をdestructorと呼び、destructorから削除演算子を呼び出すのはなぜですか?libC++でmac osのバグが発生する可能性があります。文字列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()); 
} 

入れブレークポイントを参照し、削除してから、コールスタックを確認してください。 new演算子はbasic_stringクラスから呼び出されますが、deleteがmainの最後から呼び出されますが、理想的にはbasic_string destructorが最初に呼び出されてから、deallocateの呼び出しで呼び出されるべきです。allocatorの呼び出しです。

+0

最適化を有効にしましたか?デストラクタが呼ばれているのはなぜ理想的なのですか?デストラクタの観察可能な唯一の効果は削除呼び出しであるため、これはas-ifルールによって許可されます。 – user2079303

+0

私は最適化を有効にしていません。basic_stringについては、アセンブリコードからoperator newを呼び出しますが、2番目の文字列作成の場合はallocatorを使用し、allocateメソッドでnew演算子を呼び出します。 basic_stringクラスのデストラクタを見ることもできますし、アロケータを使って文字列を作成した場合の破壊時に呼び出されるように書かれていると感じるコードを調べることもできます。 –

答えて

2

私はデバッガであなたと同じことを見ています。私は確かにわからないが、私はものがインラインになっていると思う。 basic_stringのデストラクタは非常に小さいです。小さな文字列の最適化のための単一のテスト、そしてアロケータのdeallocate関数への呼び出し(allocate_traitsを介して)。 std::allocatorのallocate関数も非常に小さく、ちょうどoperator deleteのラッパーです。

あなた自身のアロケータを書くことでこれをテストすることができます。 (後で:下記を参照)

この質問を調べている間に私が生成したその他のもの;あなたが興味を持っているならば読んでください。

[注:あなたのコードにバグがあります - あなたが書いた2行目に:? - mystr1宣言ですmystr1.begin(),mystr.end())]:

それはタイプミスだと仮定すると、私はいくつかのわずかに異なるコードを試してみました

#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()への両方の呼び出しを表示します。 (まあ、私は3つのを参照してくださいが、そのうちの1つは例外処理ブロックにあり、これは_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()); デストラクタ呼び出しと明示的なデアロケートコールの動作が違う、私のアプリケーションがDLLを使用している場所でデバッグ中に見つかりました。DLLがstringstream.str()メソッドを呼び出すときにDLLのnew演算子がコールされます上記の行を追加してdeallocateを明示的に呼び出すと、呼び出され、呼び出されたDLLの演算子が削除されます。 –

+0

libstdC++の場合、アプリケーションレベルの新規および削除演算子が呼び出されていました。 –

関連する問題