如何防止ptr_map在插入失败时释放数据

时间:2018-01-05 21:40:50

标签: c++ boost

在我的问题中    insert or update in ptr_map 我想使用以下代码

typedef boost::ptr_map<const std::string, Entry> KeyEntryMap;
KeyEntryMap m;
void insertOrUpate(const char* key, Entry* entry) {
    std::pair<KeyEntryMap::iterator, bool> re = m.insert(key, entry);
    if (!re.second) {
        m.replace(re.first, entry);
    }
}

但是,如果插入失败,ptr_map中的以下代码会插入(通过auto_type)条目对象。

    std::pair<iterator,bool> insert_impl( const key_type& key, mapped_type x ) // strong
    {
        this->enforce_null_policy( x, "Null pointer in ptr_map_adapter::insert()" );
        auto_type ptr( x );                                         // nothrow

        std::pair<BOOST_DEDUCED_TYPENAME base_type::ptr_iterator,bool>
             res = this->base().insert( std::make_pair( key, x ) ); // strong, commit      
        if( res.second )                                            // nothrow     
            ptr.release();                                          // nothrow
        return std::make_pair( iterator( res.first ), res.second ); // nothrow        
    }

我想这是因为ptr_map希望确保插入的ptr不会泄漏。这是否意味着我们必须在调用replace时创建一个新的入口对象?我们可以让ptr_map停止释放吗?

另一种方法是首先使用find。

    KeyEntryMap::iterator p = m.find(key);
    if (p == m.end()) {
        m.insert(key, entry);
        return;
    }

    m.replace(p, entry);

这是否会增加插入案例的成本?它需要两次搜索地图。

1 个答案:

答案 0 :(得分:1)

是的,这是预料之中的。指针容器取得你给它们的所有指针的所有权。

同样地,m.replace(...)释放先前存在于地图中的元素,除非你&#34; catch&#34;它:

KeyEntryMap::auto_type previous = m.replace(re.first, entry);

它是一个不变/合同,导致代码很容易正确(面对异常,而不是泄漏内存等)。

如果你愿意允许可以为空的映射类型,你可以使用一个狡猾的小技巧:

typedef boost::ptr_map<const std::string, boost::nullable<Entry> > KeyEntryMap;

struct Demo {
    KeyEntryMap m;

    void insertOrUpate(const char* key, Entry* entry) {
        auto it = m.insert(key, nullptr).first;
        /*auto previous =*/ m.replace(it, entry);
    }
};

除此之外,获取插入点,并进行检查:

void insertOrUpate(const char* key, Entry* entry) {
    auto range = m.equal_range(key);
    if (range.empty()) {
        m.insert(range.end(), key, entry);
    } else {
        m.replace(range.begin(), entry);
    }
}

这样,仍然只有一个查询。

样本

<强> Live On Coliru

#include <boost/ptr_container/ptr_map.hpp>
#include <string>

struct Entry {
    int id = [] { static int idgen = 0; return idgen++; }();
};

typedef boost::ptr_map<const std::string, Entry> KeyEntryMap;

struct Demo {
    KeyEntryMap m;

    void insertOrUpate(const char* key, Entry* entry) {
        auto range = m.equal_range(key);
        if (!range.empty()) {
            m.replace(range.begin(), entry);
        } else {
            m.insert(range.end(), key, entry);
        }
    }

    friend std::ostream& operator<<(std::ostream& os, Demo const& d) {
        for (auto e : d.m) os << e.first << ":" << e.second->id << " ";

        return os;
    }
};

#include <iostream> 

int main() {
    Demo d;
    for (auto key : { "a", "a", "c", "a", "b" }) {
        d.insertOrUpate(key, new Entry());
        std::cout << d << "\n";
    }
}

打印

a:0 
a:1 
a:1 c:2 
a:3 c:2 
a:3 b:4 c:2