用C++11中的智能指针包装旧的C结构并自动释放它们

Wrapping around old C Structures with smart pointers in C++11 and auto-freeing them

本文关键字:释放 结构 C++11 智能 指针 包装      更新时间:2023-10-16

我使用的是WordNet,这是普林斯顿大学在90年代开发的一个旧的C库。该库是用C编写的,只显示了头,但没有显示其实际实现。

我使用的唯一结构是:

SynsetPtr

我调用的两个函数是:

findtheinfo_ds
traceptrs_ds

这两个函数都返回一个SynsetPtr。

然而,当SynsetPtr表示一个感知列表时,我必须使用来释放它

free_syns

然而,当SynsetPtr用于遍历链表(层次树)时,我必须使用来释放它

free_synset

文档并不清楚何时调用哪个,以及为什么调用。

这很快就成了我的噩梦。我花了三天时间慢慢地处理泄漏、双重释放、内存分配等问题。

所以我想知道,有没有一种方法可以让我绕过这些函数,或者实际的结构,让C++管理内存?理想情况下,当没有更多引用时,我希望它们是免费的,就像std::shared_ptr的情况一样。

知道Synset_Ptr没有析构函数,但必须调用dealloc函数,这可能吗?

或者,我可以绕过创建(分配)这些结构的两个函数,以某种方式保留对象,并在没有引用的情况下销毁它们吗?

我真的很感激任何帮助!

编辑:

这是SynsetPtr在wn.h 中的确切声明

/* Structure for data file synset */
typedef struct ss {
long hereiam;       /* current file position */
int sstype;         /* type of ADJ synset */
int fnum;           /* file number that synset comes from */
char *pos;          /* part of speech */
int wcount;         /* number of words in synset */
char **words;       /* words in synset */
int *lexid;         /* unique id in lexicographer file */
int *wnsns;         /* sense number in wordnet */
int whichword;      /* which word in synset we're looking for */
int ptrcount;       /* number of pointers */
int *ptrtyp;        /* pointer types */
long *ptroff;       /* pointer offsets */
int *ppos;          /* pointer part of speech */
int *pto;           /* pointer 'to' fields */
int *pfrm;          /* pointer 'from' fields */
int fcount;         /* number of verb frames */
int *frmid;         /* frame numbers */
int *frmto;         /* frame 'to' fields */
char *defn;         /* synset gloss (definition) */
unsigned int key;       /* unique synset key */
/* these fields are used if a data structure is returned
instead of a text buffer */
struct ss *nextss;      /* ptr to next synset containing searchword */
struct ss *nextform;    /* ptr to list of synsets for alternate
spelling of wordform */
int searchtype;     /* type of search performed */
struct ss *ptrlist;     /* ptr to synset list result of search */
char *headword;     /* if pos is "s", this is cluster head word */
short headsense;        /* sense number of headword */
} Synset;
typedef Synset *SynsetPtr;

/* Primary search algorithm for use with programs (returns data structure) */
extern SynsetPtr findtheinfo_ds(char *, int, int, int); 
/* Recursive search algorithm to trace a pointer tree and return results
in linked list of data structures. */
SynsetPtr traceptrs_ds(SynsetPtr, int, int, int);
/* Free a synset linked list allocated by findtheinfo_ds() */
extern void free_syns(SynsetPtr);   
/* Free a synset */
extern void free_synset(SynsetPtr); 

这基本上就是我所知道的全部。

编辑2:

尽管我使用了下面的两个答案,但不幸的是,函数仍然在泄漏字节。

这种情况似乎只发生在:

traceptrs_ds ( ptr, SIMPTR, ADJ, 0 )

文档中几乎没有关于形容词同义词(-synsa)或其他类型(-synsn,-synsv)的信息。

然而,我只需遵循ptr->ptrlist&ptr->nexts;

traceptr_ds迭代了其中的ALL,但我找不到避免泄漏的方法,即使在使用缩小的测试程序时也是如此。

感谢所有帮忙的人,非常感谢。

您可以为此使用std::shared_ptr,因为您可以提供删除程序来释放指针。

std::shared_ptr<Synset> findTheInfo(...) {
std::shared_ptr<Synset> sp(findtheinfo(...), free_syns);
return sp;
}
std::shared_ptr<Synset> tracePtrs(...) {
std::shared_ptr<Synset> sp(traceptrs(...), free_synset);
return sp;
}

现在,如果它们真的代表了不同的东西,您可能需要花费更多的精力,并提供两种类型来包装每个使用并提供适当的接口。当转换列表和树可能完全不同时,将两者视为同一类型有意义吗?

我知道如何使用unique_ptr的漂亮功能来解决唯一所有权的这个问题,如果前一种类型存在,则其托管类型变为Deleter::pointer,而不是T*

假设您没有Synset的定义,或者SynsetPtr指向的任何类型,那么使用shared_ptr的问题在于它没有切换托管类型的相同功能,如果您创建shared_ptr<SynsetPtr>,则构造函数将需要SynsetPtr*,但您的C API函数不会返回该类型。如果没有对SynsetPtr进行解引用所产生的类型的定义,我不确定使用shared_ptr<remove_pointer<SynsetPtr>::type>是否会编译。

这个可能有效,但我不确定。

std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
make_findtheinfo_ds(char *searchstr, int pos, int ptr_type, int sense_num)
{
return 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>
make_traceptrs_ds(SynsetPtr synptr, int ptr_type, int pos, int depth)
{
return std::shared_ptr<std::remove_pointer<SynsetPtr>::type>
(traceptrs_ds(synptr, ptr_type, pos, depth),
free_synset);
}

按照唯一的所有权路线,我会制作几个返回unique_ptr管理SynsetPtr的工厂函数。

我们需要两个不同类型的SynsetPtrs 的独立删除程序

struct sense_list_del
{
using pointer = SynsetPtr;
void operator()(SynsetPtr p)
{
free_syns(p);
}
};
struct linked_list_del
{
using pointer = SynsetPtr;
void operator()(SynsetPtr p)
{
free_synset(p);
}
};
std::unique_ptr<SynsetPtr, sense_list_del>
make_findtheinfo_ds(char *searchstr, int pos, int ptr_type, int sense_num)
{
return std::unique_ptr<SynsetPtr, sense_list_del>
(findtheinfo_ds(searchstr, pos, ptr_type, sense_num));
}
std::unique_ptr<SynsetPtr, linked_list_del>
make_traceptrs_ds(SynsetPtr synptr, int ptr_type, int pos, int depth)
{
return std::unique_ptr<SynsetPtr, linked_list_del>
(traceptrs_ds(synptr, ptr_type, pos, depth));
}