这个问题与我一年前问过的一个previous question有关。我使用的是WordNet 3.0,它是很久以前用C编写的(我想大概是1996年左右)。
为了尽量远离内存管理,我询问如何才能将std::shared_ptr包装在WordNet使用的旧struct Synset上。
一个reference table显示了WordNet3.0函数,对the actual header的检查表明,函数traceptrs_ds和findtheinfo_ds返回的SynsetPtr是指向ss或Synset的指针。
在我的previous question中,我选择删除指针,然后将其包装在shared_ptr周围,如下所示:
std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
(findtheinfo_ds(searchstr, pos, ptr_type, sense_num),
free_syns);
std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
(traceptrs_ds(synptr, ptr_type, pos, depth),
free_synset);我有两个循环,一个循环通过跟随nextss迭代senses,另一个循环通过调用traceptrs_ds遍历树列表。这是两个循环(过于简单):
std::shared_ptr<Synset> synsets;
synsets = std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
(findtheinfo_ds(const_cast<char*>(key.c_str()), lexical, HYPERPTR, ALLSENSES),free_syns);
if (synsets)
{
std::shared_ptr<Synset> sense_ptr = synsets;
while(sense_ptr)
{
graph g = graph();
iterate(sense_ptr, nullptr, g, 0, lexical);
sense_ptr = std::shared_ptr<std::remove_pointer<SynsetPtr>::type>(sense_ptr->nextss);
}
}
iterate(
std::shared_ptr<Synset> ptr,
std::shared_ptr<layer> last,
graph & rhs,
int i,
int lexical
)
{
while (ptr)
{
i++;
ptr = std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
(traceptrs_ds(ptr.get(), HYPERPTR, lexical, 1), free_synset);
}
}据我所知(请随意纠正我)许多std::shared_ptr都围绕着相同的内存,当它们的析构函数被调用时,它们试图释放相同的内存。
我如何才能防止这种情况发生?
我知道findtheinfo_ds会返回一个链表,而且每次调用traceptrs_ds都会迭代这个链表。我认为最好的方法是只包装一个将调用Synset析构函数free_synset的std::shared_ptr,而不是多次这样做。
我如何才能阻止这种情况的发生?
假设没有解决方案来防止这种情况发生,那么映射到同一区域的多个std::shared_ptr的析构函数怎么可能只释放一次呢?我猜这是一种糟糕/丑陋的方式,但它可能更容易?
valgrind输出:
==3066== Invalid read of size 8
==3066== at 0x52C3B4C: free_synset (in /usr/lib/libwordnet-3.0.so)
==3066== by 0x4E6E2D9: std::_Sp_counted_deleter<ss*, void (*)(ss*), std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:463)
==3066== by 0x403C5F: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:149)
==3066== by 0x403AD8: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr_base.h:666)
==3066== by 0x4E61DB7: std::__shared_ptr<ss, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr_base.h:914)
==3066== by 0x4E61DD1: std::shared_ptr<ss>::~shared_ptr() (shared_ptr.h:93)
==3066== by 0x4E6161A: smnet::hyper_handler::operator()(std::string, int) (hyper_handler.cpp:22)
==3066== by 0x4036D6: main (hypernym.cpp:16)
==3066== Address 0x10 is not stack'd, malloc'd or (recently) free'd
==3066== Process terminating with default action of signal 11 (SIGSEGV)
==3066== Access not within mapped region at address 0x10
==3066== at 0x52C3B4C: free_synset (in /usr/lib/libwordnet-3.0.so)
==3066== by 0x4E6E2D9: std::_Sp_counted_deleter<ss*, void (*)(ss*), std::allocator<void>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:463)
==3066== by 0x403C5F: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:149)
==3066== by 0x403AD8: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr_base.h:666)
==3066== by 0x4E61DB7: std::__shared_ptr<ss, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr_base.h:914)
==3066== by 0x4E61DD1: std::shared_ptr<ss>::~shared_ptr() (shared_ptr.h:93)
==3066== by 0x4E6161A: smnet::hyper_handler::operator()(std::string, int) (hyper_handler.cpp:22)
==3066== by 0x4036D6: main (hypernym.cpp:16)
==3066== LEAK SUMMARY:
==3066== definitely lost: 0 bytes in 0 blocks
==3066== indirectly lost: 0 bytes in 0 blocks
==3066== possibly lost: 251 bytes in 8 blocks
==3066== still reachable: 19,964 bytes in 90 blocks
==3066== suppressed: 0 bytes in 0 blocks
==3066== Reachable blocks (those to which a pointer was found) are not shown.
==3066== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==3066==
==3066== For counts of detected and suppressed errors, rerun with: -v
==3066== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
fish: “valgrind --leak-check=full ./hy…” terminated by signal SIGSEGV (Address boundary error)发布于 2016-01-08 03:34:54
嗯,经过相当多的搜索,我找到了使用struct functors和lambda的解决方案。诚然,它不是很漂亮(事实上它相当丑陋),但看起来很管用:
struct sense_del
{
void operator()(Synset *ptr)
{
if (ptr)
free_syns(ptr);
}
};
struct trace_del
{
void operator()(Synset *ptr)
{
if (ptr)
free_synset(ptr);
}
};
std::shared_ptr<Synset>
shared_findtheinfo_ds(std::string key, int gram_type, int ptr_type, int sense_num)
{
wn_init_once();
std::shared_ptr<Synset> sp(findtheinfo_ds(const_cast<char*>(key.c_str()), gram_type, ptr_type, sense_num),
[](Synset * ptr){sense_del()(ptr);});
return sp;
}
std::shared_ptr<Synset>
shared_traceptrs_ds(SynsetPtr synptr, int ptr_type, int gram_type, int depth)
{
assert(synptr);
std::shared_ptr<Synset> sp(traceptrs_ds(synptr, ptr_type, gram_type, depth),
[](Synset *ptr){trace_del()(ptr);});
return sp;
}仅供参考,对于任何在Wordnet上工作的人来说,Hypernyms的正确迭代方式是:
if (auto ss_ptr = findtheinfo_ds(const_cast<char*>(key.c_str()), lexical, HYPERPTR, ALLSENSES))
{
while(ss_ptr)
{
if (SynsetPtr trc_ptr = traceptrs_ds(ss_ptr, HYPERPTR, lexical, 1))
{
do_smth_with_it(trc_ptr);
free_syns(trc_ptr);
}
// get next sense
ss_ptr = ss_ptr->nextss;
}
free_syns(ss_ptr);
}https://stackoverflow.com/questions/34644634
复制相似问题