1

我有一个对象的 std::map,其实例的构造成本非常高。(在现实生活中,他们需要多次访问数据库。)

我想访问地图的一个元素,或者如果它不存在就创建它。这听起来像是 std::map::insert 的工作,除了昂贵的对象是不必要地构造的,然后如果元素存在则丢弃。为了显示:

#include <iostream>
#include <map>
#include <string>

struct CexpensiveObject
{    
    CexpensiveObject(const char* args="default"):args_(args)
    {
        std::cout << "Constructor: CexpensiveObject(" << args << ")" << std::endl;
    }
    CexpensiveObject( const CexpensiveObject& other )
    {
        std::cout << "Copy Constructor: CexpensiveObject other.args_ = " << other.args_ << "." << std::endl;
        args_ = other.args_;
    }
    ~CexpensiveObject()
    {
        std::cout << "Destructor: CexpensiveObject args_ = " << args_ << "." << std::endl;
    }
    const char* args_;
};

// entry point
int main() 
{
    typedef std::map<std::string, CexpensiveObject> mymaptype;   
    mymaptype mymap;
    std::pair<mymaptype::iterator, bool> insertionResult;

    std::cout << "First insertion" << std::endl;
    insertionResult = mymap.insert( mymaptype::value_type( "foobar", CexpensiveObject("first") ) );
    std::cout << "Was it inserted? " << (insertionResult.second?"yes":"no") << std::endl;

    std::cout << "Second insertion" << std::endl;
    insertionResult = mymap.insert( mymaptype::value_type("foobar", CexpensiveObject("second") ) );
    std::cout << "Was it inserted? " << (insertionResult.second?"yes":"no") << std::endl;
}

结果:

First insertion
Constructor: CexpensiveObject(first)
Copy Constructor: CexpensiveObject other.args_ = first.
Copy Constructor: CexpensiveObject other.args_ = first.
Destructor: CexpensiveObject args_ = first.
Destructor: CexpensiveObject args_ = first.
Was it inserted? yes
Second insertion
Constructor: CexpensiveObject(second)
Copy Constructor: CexpensiveObject other.args_ = second.
Destructor: CexpensiveObject args_ = second.
Destructor: CexpensiveObject args_ = second.
Was it inserted? no
Destructor: CexpensiveObject args_ = first.

复制和破坏的次数比我预期的要多,但关键是构造了一个实例 CexpensiveObject,然后如果 ma 中存在具有相同键的元素,则将其丢弃。

我是在滥用 std::map::insert,还是在实例化 CexpensiveObject 实例之前必须使用 std::map::find 来检查是否存在具有相同键的元素?

4

2 回答 2

9

insert它是在你调用之前构建的CexpensiveObject("second")。您正在传递无关的对象!value_type(然后它在传递给时被复制insert。)

而不是insert,使用find. 如果您在所需的键处找到该项目,那么您就完成了。如果没有,插入它。

auto it = mymap.find("foobar");
if (it == mymap.end())
  mymap.insert(mymaptype::value_type("foobar", CexpensiveObject("second")));
于 2012-12-05T20:09:12.510 回答
1

如果对象已经存在,使用 find 检查是否应该插入元素以节省一些工作。

可悲的是 map::insert 多次调用您的复制构造函数。如果这是不可接受的,并且您可以使用 c++11,请查看 map::emplace_insert。如果您必须使用 C++03,IIRC升压容器有一个地图,它使用内部升压移动来模拟移动语义。

于 2012-12-05T20:35:24.783 回答