2012-03-12 10 views
6

私はboost :: make_sharedを初めて使用して、共有ポインタが指すオブジェクトを作成しています。主にコードが低すぎて単一の割り当てが実際にパフォーマンスを向上させるのに役立ったためです。boost :: make_sharedが(配置)演算子newを呼び出さない

いくつかのメモリリークを修正した後、私は単純なメモリリークディテクタを実装することを決めました。アプリケーションの特定のポイントでどのオブジェクトがまだ生存しているかをカウントするだけです。これを数回前に実装していて、自分のコードがオブジェクトを検出しなくなったことに驚きました。

私は私がしなければならなかったすべては上書き「配置新しい」ためmake_shared用ブーストウェブサイトのドキュメントから、次の代わりに、「正常な」オペレータ新者のあることを考え出し:

「エフェクト:メモリを割り当て、適切なタイプT と のオブジェクトに対しては、配置式new(pv) T()または新しい(pv)T(std :: forward(args)...)を使用してオブジェクトを構成します。 allocate_shared は、メモリの割り当てにaのコピーを使用します。例外がスローされた場合は、 の効果はありません。

新しいプレースメントも呼び出されません。私は、出力の3行目に「テストは、新しい配置を非投げ」期待していた

... 
Global new 
Test::Test() 
... 
Test new 
Test::Test() 
Global new 
... 

:以下の出力をレンダリングする

#include <iostream> 
using namespace std; 
#include "boost/shared_ptr.hpp" 
#include "boost/make_shared.hpp" 

class Test 
{ 
public: 
    Test() { cout << "Test::Test()" << endl; } 

    void* operator new (std::size_t size) throw (std::bad_alloc) { 
     cout << "Test new" << endl; 
     return malloc(size); 
    } 

    void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw() { 
     cout << "Test non-throwing new" << endl; 
     return malloc(size); 
    } 

    void* operator new (std::size_t size, void* ptr) throw() { 
     cout << "Test non-throwing placement new" << endl; 
     return malloc(size); 
    } 
}; 

void* operator new (std::size_t size) throw (std::bad_alloc) { 
    cout << "Global new" << endl; 
    return malloc(size); 
} 

int main() { 
    cout << "..." << endl; 
    boost::shared_ptr<Test> t1(boost::make_shared<Test>()); 
    cout << "..." << endl; 
    boost::shared_ptr<Test> t2(new Test()); 
    cout << "..." << endl; 

    return 0; 
} 

:私は動作を再現するための小さなテストプログラムを書かれています。その行動はどうあるべきだと思いますか? make_sharedのドキュメントによれば、Testクラスのプレースメントnew演算子を呼び出す必要があることに同意しますか?それとも私はそれを誤解しましたか?

私はブーストの実装をローカルにコピーして、もちろん、プレースメントnew演算子への呼び出しを追加することができます。しかし、それは適切であろうか、それとも新しい配置のセマンティクスに違反するだろうか?

お時間をいただきありがとうございます。

+0

ブーストのを見ると、グローバルプレースメントnew演算子:: new(pv)T()が使用されています。そのため、クラスレベルの配置が呼び出されていないのです... newの前にグローバル修飾子 '::'を削除すると、make_sharedは実際にクラスレベルの配置new演算子を呼び出します。 – Gob00st

答えて

8

make_sharedのソースとして見ると、クラスによって提供されるnew演算子ではなく、グローバル配置new演算子が使用されます。

::new(pv) T(); 

残念ながら(according to the standard)(OS X上の少なくともとして)、あなたがあなた自身のグローバルな配置new演算子を定義することはできません allocate_sharedは、あなたが探している線のほうにあるようです。

編集

代替はなく、実際に世界1の新しいクラスの配置を使用していますmake_sharedのバージョンを書くことである可能性があります。に敬意を表している限り、コードは約10行しかないので、うまくいくはずです。あなたの特定のタイプのために実装

+0

'allocate_shared()'は、より大きなコードベースでかなりの数の呼び出しを置き換えることを意味することに注意してください。 –

+0

便利な答えをありがとう。私は新しいプレースメントをオーバーライドしてはならないとは知らなかった。だから、これは間違いなく、私が解決策を見出そうとしているショーストッパーです。私はすでにallocate_sharedオプションを調査しましたが、Georgianが述べたように、これは既存のコードにあまりにも大きな影響を与えます。 –

4

プレースメント新品(§18.4。1.3、例:this questionを参照)を交換することはできません。

ブーストヘッダーを変更する代わりに、Valgrindのような外部ツールを調べることができます。

3

あなたoperator newは、あなたのタイプの要素は動的なTest *p = new Test;として、newを割り当てられている上での表現に使用されます。今make_sharedは動的あなたのタイプのオブジェクトではなく、あなたのオブジェクト(カウンタ、deleterは、余分なこまごまとしたいくつかが含まれます)共有数のための十分な情報を保持しているバッファを割り当てませありません。

次に、プレースメントnewを使用して、オブジェクトのコンストラクタを呼び出します。この場合、配置の新しいはメモリを割り当てていないことに注意してください。既に割り当てられたメモリのブロックでコンストラクタを呼び出すのは面白い構文です。これは実際には、newの式であるoperator newのプレースメントnewは、名前を共有する3つの異なる概念です。

+0

それにもかかわらず、placement-newに対してもクラスごとのオーバーロードを提供することは可能です。おそらく、おそらくC++ *のどの部分が異常ではないのでしょうか:-)家に持ち帰る主なポイントは、 'std :: allocator'はクラスごとの割り当て関数を使いませんが、 :: new' –

関連する問題