【问题标题】:Memory Management for Lookup Tables with string keys in C++C++ 中带有字符串键的查找表的内存管理
【发布时间】:2015-12-29 01:45:34
【问题描述】:

我正在用 C++ 实现一个双键(整数和字符串)查找表,我想知道我应该如何为它编写一个析构函数。

查找表 API 允许用户将命名对象插入和查找到表中。在幕后,一个唯一的 int 键会自动分配给包含对象名称的字符串。插入后可以通过字符串名称或int值查找插入的对象。

这里是实现的要点。

template <class VALUE>
class COLLECTION
{
    public;
    typedef int IDX;
    typedef std::map< std::string , IDX >   NAME_TO_IDX;
    typedef std::map< IDX, std::string >   IDX_TO_NAME;
    typedef std::map<IDX, VALUE*>          IDX_TO_VAL;
...
    void insert( const std::string name, VALUE* v )
    {   
        m_name_to_idx_map[name]       = m_next_idx;
        m_idx_to_name_map[m_next_idx] = name;
        m_idx_to_val_map[m_next_idx] = v;
        ++m_next_idx;
    }
    VALUE* get_value( const IDX idx ) { return m_idx_to_val_map[idx]; }
    VALUE* get_value( const std::string name )
    ....
    private:
    int           m_next_idx;
    NAME_TO_IDX   m_name_to_idx_map;
    IDX_TO_NAME   m_idx_to_name_map;
    IDX_TO_VAL    m_idx_to_val_map;

如何为这个类编写析构函数? 谢谢

【问题讨论】:

  • 绝对:void insert(const std::string&amp; key, std::unique_ptr&lt;VALUE&gt; value),宾果游戏。如果你讨厌在代码中表达需求,你也可以传递一个原始指针并在函数声明上方添加一个注释 // Takes owernship of *value and calls "delete" on the pointer.。 (重要的是要明确,用户不得传递从malloc 或数组-new 获得的指针,因此仅“获取所有权”不够精确。但唯一的指针不会引起歧义。)
  • 对于m_idx_to_name_mapm_idx_to_val_map,最好(平均而言)使用vectors 而不是maps,因为索引是连续的(假设您不支持删除)。
  • 为什么你会有这种看似毫无意义的间接性和你的类的混合责任也是值得怀疑的。为什么不只是有一个map&lt;int, VALUE&gt;?如果用户关心间接,她可以使用VALUE = std::unqiue_ptr&lt;X&gt;
  • 您也可以考虑使用boost::bimap 进行此类双向映射。
  • Kerrek 的职责分离建议是迄今为止最好的选择,unique_ptr 是第二个,但 FWIW 另一个是有一个模板策略参数或构造函数参数指定一个可以调用的函子取消分配(如有必要)VALUE*。另外,最好为预处理器宏保留全大写标识符,但可用于模板参数的单字母标识符除外。

标签: c++ string memory-management


【解决方案1】:

为避免在析构函数中手动释放所有 VALUE* 拥有的内存 - 或许您应该使用智能指针。

C++11 提供了std::shared_ptr,你可以用它来存储VALUE*

typedef std::map<IDX, std::shared_ptr<VALUE> > IDX_TO_VAL;

如果你可以使用 boost 库,那么你可以使用boost::shared_ptr

【讨论】:

    【解决方案2】:

    唯一的堆分配部分可能是 VALUE*(不是大写的忠实粉丝)。 对于您的析构函数,您必须遍历映射并删除所有相关指针,如下所示

    for(auto iter = IDX_TO_VAL.begin(); iter != IDX_TO_VAL.end(); ++iter)
    {
        delete (*iter).second;
    }
    

    【讨论】:

    • @unshul 如果 VALUE 指向在堆上分配的东西怎么办? -- 如果不是呢?如果您尝试在该值上调用 delete,则会出现未定义的行为。这就是您的设计中的缺陷——您无法假设您在地图上填充的地址来自哪里。
    猜你喜欢
    • 2010-10-11
    • 1970-01-01
    • 2017-01-31
    • 1970-01-01
    • 1970-01-01
    • 2014-10-10
    • 1970-01-01
    • 2011-10-22
    • 2016-02-05
    相关资源
    最近更新 更多