使用C++ map作为关联数组需要帮助理解

7
我正在阅读Josuttis的“使用Map作为关联数组”(来自C++标准库-教程和参考,第二版),并在Stack Overflow上发现了将std::map用作关联数组。现在我对插入到map中调用的构造函数有更多疑问。
这是我的示例程序(不使用最佳编码实践,请原谅我):
class C
{
public:

   string s;

   C() { cout << "default " << endl;}

   C(const string& p) : s(p)
   { cout << "one param" << endl;}

   C(const C& obj)
   {
       if (this != &obj)
       {
         s = obj.s;
       }
       cout << "copy constr" << endl;
   }

   C& operator  = (const C& obj)
   {
       if (this != &obj)
       {
             s = obj.s;
       }
      cout << "copy initializer" << endl;
      return *this;
   }
};

int main()
{
    map<int,C> map1;
    C obj("test");

    cout << "Inserting using index" << endl;
    map1[1] = obj;

    cout << "Inserting using insert / pair" << endl;
    map1.insert(make_pair(2,obj));
}

这个程序的输出是:
one param
Inserting using index
default
copy constr
copy constr
copy initializer
Inserting using insert / pair
copy constr
copy constr
copy constr
copy constr

我原以为通过索引初始化map应该调用默认构造函数,然后再调用赋值运算符。但执行map1[1] = obj会产生以下输出;
Inserting using index
default
copy constr
copy constr
copy initializer

有人可以帮我更好地理解初始化吗?


你不需要测试自我复制构造。 - Martin York
事实上,我现在明白,在STL容器中使用指针比对象更好。 - mithuna
1
@mithuna: 警告:存储指针而不是对象会带来生命周期管理问题,因此我建议最好存储对象。如果这会导致性能问题,你应该看看能否对拷贝进行优化。然后,如果不能进行优化,你可以考虑存储指针而不是对象。在这种情况下,你可能会对Boost.PointerContainer库(http://www.boost.org/doc/libs/1_40_0/libs/ptr_container/doc/ptr_container.html)感兴趣。在当前情况下,使用ptr_map (http://www.boost.org/doc/libs/1_40_0/libs/ptr_container/doc/ptr_map.html)。 - Éric Malenfant
...或者,如果您有可用的实现支持C++0x移动语义,请使用它。 - Éric Malenfant
4个回答

8

如果您阅读std::map的规范,它会说明operator[]等价于(在本例中)

(*((this->insert(make_pair(1,C()))).first)).second

所以这解释了你看到的所有构造函数调用。首先它调用默认构造函数C()。然后它调用make_pair,复制了C对象。接着它调用insert,又制作了一份刚才所做的pair对象的拷贝,并再次调用了C的拷贝构造函数。最后,它调用赋值运算符将插入的对象设置为你要分配的对象。

和我的答案一样,只是更好!+1并删除我的。 - Éric Malenfant
很好的回应,重新阅读规范后现在确实让人感到很有意义。 - mithuna
假设我们使用某个-O选项,只进行一次复制构造函数调用,这是否合理?请注意,为了获得更好的性能,可以使用“iterator insert(iterator pos,value_type const&x)”其中“pos”可以使用“iterator lower_bound(key_type const&)”推断出来(如果有效,则使用其前任)。因此,您将避免默认构造和赋值,同时不会陷入双重搜索陷阱。 - Matthieu M.

2
我不知道。 但这很有趣:
#include <string>
#include <map>
#include <iostream>
using namespace std;

class C
{
    public:
        string s;
        C()
        {
            cout << "default " << endl;
        }
        C(const string& p)
        : s(p)
        {
            cout << "one param(" << s << ")" << endl;
        }
        C(const C& obj)
            :s(obj.s)
        {
           cout << "copy constr(" << s << ")" <<endl;
        }
        C& operator  = (const C& obj)
        {
            cout << "copy initializer\t" <<;

            C copy(obj);
            std::swap(s,copy.s);

            return *this;
        }
};

int main()
{
    map<int,C> map1;
    cout << "Inserting using index" << endl;
    map1[1] = C("Plop");
}

看起来默认的是被创建并复制的。
然后,一旦外部的被放置好了,它只是被分配给默认的。

Inserting using index
default
copy constr()
copy constr()
one param(Plop)
copy initializer      copy constr(Plop)

0
如果你只是执行map[1];会发生什么?这可能涉及内部复制,具体取决于你的标准库使用的map实现。

0

实际上,map1[1] = obj 会先创建一个 pair


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接