与VS2013相比,gd 4.7.2中的std :: map实现效率非常低?

时间:2013-11-04 10:31:46

标签: c++ linux windows gcc c++11

有人可以帮我理解std :: map容器是如何实现的吗?我有一个包含原子成员的类,我不需要调用复制构造函数,因此我使用c ++ 11 delete运算符来禁止隐式生成复制构造函数。

MyCalss(const MyClass& a) = delete;

这对我的Windows构建工作正常,但是在Linux中我遇到一个错误,告诉我std :: map类的[]运算符正在尝试调用已删除的函数。

Windows VS2013和Linux GCC 4.7.x地图实现之间似乎存在重大差异。这导致我做了一个关于如何将对象插入地图的实验。

我写了这个小例子程序:

#include <stdlib.h>
#include <stdio.h>
#include <map>
#include <iostream>
#include <string>

using namespace std;
class TestItem {
public:
TestItem () { 
    _name = "TestItem" + id();
    cout << "Constructing " << _name << endl;
}
TestItem (const TestItem & other) {
   _name = "TestItem " + id();
   cout << "Copying " << other._name << " to new  " << _name <<endl;
}

string id() 
{
   static int id = 0;
   char buf[2];
   sprintf_s(buf, "%d", id++);
   return string(buf);
}
~TestItem(){
   cout << "Destroying " << _name << endl;
}
void doStuff()
{
   // stub
}

string _name;
};

void run()
{
   cout << "making new obj" << endl;
   TestItem a;
   cout << endl << endl;

   map<string, TestItem> TestItemMap;
   cout << "Makeing new obj as part of a map insert" << endl;
   TestItemMap["foo"].doStuff();
   cout << endl << endl;

   cout << "adding a value to the map" << endl;
   TestItemMap["new foo key"] = a;
   cout << endl << endl;

   cout << "looking up a value that has already been inserted" << endl;
   TestItem& b = TestItemMap["foo"];
   cout << endl << endl;
}
int main(int argc, char** argv)
{
   run();
}

在Windows中运行此程序时,我得到以下输出:

making new obj
Constructing TestItem0

Making new obj as part of a map insert
Constructing TestItem1

adding a value to the map
Constructing TestItem2

looking up a value that has already been inserted

Destroying TestItem1
Destroying TestItem0
Destroying TestItem0

这是我期望看到的内容,当我写

 TestItemMap["foo"].doStuff();

我希望map会创建一个新的TestItem实例,然后通过内部链接树节点到新的TestItem将其插入到RedBlack树中。

然而,当我在Linux中运行相同的代码时,结果是非常不同的

making new obj
Constructing TestItem0

Making new obj as part of a map insert
Constructing TestItem1
Copying TestItem1 to new TestItem2
Copying TestItem2 to new TestItem3
Destroying TestItem2
Destroying TestItem1

adding a value to the map
Constructing TestItem4
Copying TestItem4 to new TestItem5
Copying TestItem5 to new TestItem6
Destroying TestItem5
Destroying TestItem4

looking up a value that has already been inserted

Destroying TestItem0
Destroying TestItem3
Destroying TestItem0

这将向我表明[]运算符正在创建一个新的TestItem实例,然后调用外部map.insert()函数,然后销毁新创建的TestItem,这只解释了对复制构造函数的一个调用。 gcc中的c ++ stdlib真的效率低吗?

人们用它来克服这个问题吗?

2 个答案:

答案 0 :(得分:3)

首先,我解决了那个可怕的sprintf_s事情:

string id() 
{
   static int id = 0;
   std::stringstream s;
   s << id++;
   return s.str();
}

并且还改变了你的“查找已经插入的值”以实际执行它所说的内容[编辑:你也是如此:-)]

现在,在C ++ 03模式下使用g ++ 4.8.1进行编译,我得到的结果与您的类似。但是使用-std=c++11进行编译,我得到了

making new obj
Constructing TestItem0

Making new obj as part of a map insert
Constructing TestItem1

adding a value to the map
Constructing TestItem2

looking up a value that has already been inserted

Destroying TestItem0
Destroying TestItem1
Destroying TestItem0

似乎MSVC自动使用C ++ 11功能(最有可能移动语义)以提供良好的性能提升,而您需要明确告诉g ++也这样做。

答案 1 :(得分:2)

这似乎是用GCC 4.8修复的错误。

  • Here适用于GCC 4.8
  • Here无法使用GCC 4.7进行编译