如何让stl map只构造/破坏插入对象一次

时间:2010-06-06 16:41:00

标签: c++ stl

我发现了一个关于stl地图的非常偏见的事实。出于某种原因,我无法在地图中插入对象以仅构造/销毁一次。

示例:

struct MyObject{
    MyObject(){
        cout << "constructor" << endl;
    }
    ~MyObject(){
        cout << "destructor" << endl;
    }
};
int main() {
    std::map<int, MyObject> myObjectsMap;
    myObjectsMap[0] = MyObject();
    return 0;
}

返回:

constructor
destructor
destructor
constructor
destructor

如果我这样做:

typedef std::pair<int, MyObject> MyObjectPair;
myObjectsMap.insert( MyObjectPair(0,MyObject()));

返回:

constructor
destructor
destructor
destructor

我正在插入对自己的内存分配负责的对象,所以当它们被破坏时它们会自行清理,多次被破坏会给我带来一些麻烦。

5 个答案:

答案 0 :(得分:6)

我建议你添加一个复制构造函数 - 这就是我认为用于'缺失'构造的东西。

代码:

#include <iostream>
#include <map>

using namespace std;

struct MyObject{
    MyObject(){
        cout << "no-arg constructor" << endl;
    }
    MyObject(const MyObject&) {
    cout << "const copy constructor" << endl;
    }
    ~MyObject(){
        cout << "destructor" << endl;
    }
};
int main() {
    std::map<int, MyObject> myObjectsMap;
    myObjectsMap[0] = MyObject();
    return 0;
}

输出:

no-arg constructor
const copy constructor
const copy constructor
destructor
destructor
no-arg constructor
destructor
destructor

答案 1 :(得分:4)

std::map可以根据需要制作任意数量的对象副本。这是实现定义的,您无法控制它。顺便说一下,你注意到的“缺失”结构可能是为了调用你没有定义的复制构造函数。

然而,你可以做的是使用flyweight,因此构造一个对象实际上从一个预先存在的对象池中获取一个现有对象,并且破坏一个对象什么都不做。池永远不会释放它的对象,但它始终保持对所有对象的处理。这样,从开始到结束,你的内存使用量很大,但在程序的整个生命周期内都没有太大变化。

答案 2 :(得分:2)

为了能够在标准容器中使用,您的对象必须是可复制和可分配的。如果您的对象不符合这一点,您可能会遇到问题。

那就是说,如果(如你的示例代码所示)你只需要在地图中插入默认的构造对象,你可以使用operator []来产生副作用:

// Insert default constructed MyObject at key 0
myObjectsMap[0];

修改

我对你的问题不是很清楚,但是如果你不清楚构造的对象的数量并且认为存在构造函数/析构函数不匹配,那么请注意编译器将提供一个不会记录到的复制构造函数std::cout因为您没有提供用户声明的。

答案 3 :(得分:2)

当你说myObjectsMap [0]时,你正在调用MyObject的默认构造函数。这是因为[0]中没有任何内容,您只是访问它。 It's in the manual

当你点击MyObject();您正在使用默认构造函数创建临时MyObject实例。

因为您允许编译器定义复制构造函数,所以您会看到比构造函数消息更多的析构函数。 (只有一个析构函数,但是很多构造函数,不像构建一个房子。)如果你不应该以这种方式复制对象,那么你可能想要声明一个私有复制构造函数和复制赋值运算符。

您使用以下代码调用默认构造函数和复制构造函数两次

myObjectsMap[0] = MyObject();

执行此操作时:

myObjectsMap.insert( MyObjectPair(0,MyObject()));

你调用默认构造函数一次,复制构造函数调用3次。

您应该使用指针作为地图值而不是对象本身,特别是我建议查看shared_ptr。

note: tests were done using GCC 3.4.5 on a Windows NT 5.1 machine.

答案 4 :(得分:1)

这就是map和其他容器的工作方式,你无法解决它。这就是为什么std::auto_ptr不能在集合中使用的原因,例如。