1

編集:将来の使用のためにこの質問を読んでいる人へ:エラーは決してunique_ptrとは関係ありませんでした。JoergBが彼の答えで正しく言っているように、基本クラスの仮想デストラクタを忘れるのは私の側の間違いでした。


時折実行時にクラッシュした後、私は自分のコードがメモリリーク炎の深刻なケースに苦しんでいることに気づきました。私はValgrindでプログラムを実行しましたが、医師は同意しているようです。バイトは間違いなく失われます。しかし、私は一生の間、それがどこでうまくいかないのか理解できません。

私は、リークを次の3つの行で発生するように特定することができました。

std::unique_ptr<Operator> pointer(new Operator{"left", "right"});
NodeSpace space; // The node space takes ownership over the operator

// When I comment out the following line, Valgrind reports nothing:
space.setNode("key", move(pointer));

最初の行で、クラスunique_pointerのインスタンスを保持するaが作成されます。Operator内部的には次のOperatorようになります。

class Operator : public Node {
public:
    Operator(std::initializer_list<std::string> input_keys) {
        input_nodes_.reserve(input_keys.size());
        for_each(begin(input_keys), end(input_keys), [this](const string& key) {
            input_nodes_[key] = nullptr;
        });
    }

    // ...

private:
    std::unordered_map<std::string, Node*> input_nodes_;
};

unique_ptr次の関数にr値参照を渡します。

void NodeSpace::setNode(const std::string& key, std::unique_ptr<Node> node);

ノードスペースは渡されたノードの所有権を引き継ぐため、unique_ptr値による(移動セマンティクス)を取ります。内部的にはポインタをに格納しますがstd::map、関数本体がコメント化されていても、メモリリークが発生します(問題はnode関数呼び出しの引数であると私は信じています)。

問題がどこにあるのかをリモートで知っている人はいますか?

補足:shared_ptrノードは循環的に相互に参照できるため、私はを使用していません。weak_ptrオプションである可能性がありますが、システムの構築により、ノードを所有するノードスペースが存在しなくなったときにノードが存在することは不可能です。

Valgrindの出力:

==83791== 112 (16 direct, 96 indirect) bytes in 1 blocks are definitely lost in loss record 606 of 794
==83791==    at 0x100060ABD: malloc (vg_replace_malloc.c:274)
==83791==    by 0x1000C9147: operator new(unsigned long) (in /usr/lib/libc++.1.dylib)
==83791==    by 0x10000CB0F: std::__1::__hash_table<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*>, std::__1::__unordered_map_hasher<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::__unordered_map_equal<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*> > >::__rehash(unsigned long) (in ./test/mimi)
==83791==    by 0x10000C684: std::__1::__hash_table<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*>, std::__1::__unordered_map_hasher<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::__unordered_map_equal<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*> > >::rehash(unsigned long) (in ./test/mimi)
==83791==    by 0x100005D5A: mimi::Operator::Operator(std::initializer_list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >) (in ./test/mimi)
==83791==    by 0x100005984: mimi::Operator::Operator(std::initializer_list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >) (in ./test/mimi)
==83791==    by 0x10002E940: main (in ./test/mimi)

Operatorまた、呼び出されるまでに演算子が既に構築されているにもかかわらず、Valgrindがコンストラクターでリークが発生することを教えてくれるように見える理由もわかりませんNodeSpace::setNode()

4

1 に答える 1

5

Nodeコードの重要な部分、特にそのデストラクタの宣言、の一部NodeSpace、特にsの削除方法Nodeなどは表示されません。ただし、コメントから「ノードにもオペレータにもデストラクタとコピー/移動コンストラクタはありません。 / Assignment-operator」、それが問題のようです。

Operatorを介して所有権を維持するNode *場合、つまり、unique_ptr<Node>を介して派生オブジェクトを削除する場合は、仮想デストラクタが必要です。宣言して定義しないと、宣言されません。Node *Node

結果として生じる動作は定義されていませんが、結果は通常、派生クラスのメンバーのデストラクタが呼び出されないということです。それはあなたのエラーメッセージと一致します。

于 2013-01-27T14:15:10.760 に答える