【问题标题】:Destruction of an object when erasing it from std::map从 std::map 擦除对象时破坏对象
【发布时间】:2013-08-07 08:29:09
【问题描述】:

当我从std::map 中删除一个元素时,如果调用了默认析构函数,我很好奇。这是我做的一个例子:

class CTestMap{
public:
    CTestMap() {
        std::cout << "default constructor called" << std::endl;
    }
    CTestMap(int id) {
        std::cout << "created object: " << id << std::endl;
        m_id = id;

    }
    ~CTestMap() {
            std::cout << "destroyed object: " << this->m_id << std::endl;
    }
    int get_id(){
        return m_id;
    }
    int m_id;
};

int main(void){

    std::map<int, CTestMap>m;
    std::map<int, CTestMap>::iterator m_it;

    std::cout << "created map " << std::endl;

    CTestMap t1(1);
    std::cout << "created test object: " << t1.get_id() << std::endl;
    CTestMap t2(2);
    std::cout << "created test object: " << t2.get_id() << std::endl;
    CTestMap t3(3);
    std::cout << "created test object: " << t3.get_id() << std::endl;

    m[1] = t1;
    m_it = m.find(1);
    std::cout << "inserted test object: " <<  m_it->second.get_id() << std::endl;

    m[2] = t2;
    m_it = m.find(2);
    std::cout << "inserted test object: " <<  m_it->second.get_id() << std::endl;

    m[3] = t3;
    m_it = m.find(3);
    std::cout << "inserted test object: " <<  m_it->second.get_id() << std::endl;

    m_it = m.find(1);
    std::cout << "will now erased test object: " << m_it->second.get_id() << std::endl;
    m.erase(m.find(1));
    std::cout << "erased test object: " << m[1].get_id() << std::endl;

    m_it = m.find(1);
    std::cout << "object shall no longer exist: " << m_it->second.get_id() << std::endl;


    while(1);
return 0;
}

这里是输出:

./htest
created map
created object: 1
created test object: 1
created object: 2
created test object: 2
created object: 3
created test object: 3
default constructor called
destroyed object: 9377935
destroyed object: 9377935
inserted test object: 1
default constructor called
destroyed object: 9377935
destroyed object: 9377935
inserted test object: 2
default constructor called
destroyed object: 9377935
destroyed object: 9377935
inserted test object: 3
will now erased test object: 1
destroyed object: 1
default constructor called
destroyed object: 158830600
destroyed object: 158830600
erased test object: 158830600
object shall no longer exist: 158830600

问题是:

  1. 为什么调用了这么多次默认构造函数,而我只是 使用我自己的构造函数创建 3 个对象?
  2. 我可以吗,基于 这个例子说,每次我从 std::map,它的析构函数叫做 ?这是一个普遍的行为吗 std::map ?我找不到此信息。
  3. 如果我存储指向对象的指针(我使用'new' 运算符创建它们)怎么办?那么什么时候应该调用delete

【问题讨论】:

  • 让你的拷贝构造函数和拷贝赋值运算符也打印出来,你会看到的。如果您的编译器支持移动语义,请对移动构造函数和移动赋值运算符执行类似操作。
  • 您可能需要创建一个复制构造函数来更好地理解这一点。
  • @MarkGarcia 即使编译器支持移动语义,如果对象具有用户定义的复制构造函数,它也不会使用它们。只需定义复制构造函数就足以初步了解。 (std::map 从来不使用赋值,我认为在 C++11 中已删除了 Assignable 要求。)
  • @MarkGarcia 当然,表达式m[1] = t1; 使用赋值运算符(在std::map 之外)。

标签: c++ std stdmap erase


【解决方案1】:

std::map 存储您插入的对象的副本。当。。。的时候 对象被删除,正是这个副本被破坏。所以 在m[1] = t1; 之后,有两个相同的实例 CTestMap: t1 和地图上的那个。

另外:m[1] = t1; 将首先在地图中创建一个新条目, 使用默认构造函数,然后将t1 分配给它。

一般来说,如果你想像这样跟踪实例的生命周期, 您需要提供用户定义的复制构造函数和赋值 操作符也可以跟踪。你可能想输出 所有跟踪中的this 指针。 (另一种技术 将是用不可变的唯一性来宠爱每个对象 标识符:

#define TRACE(m) std::cout << #m << '(' << m_objectId << ')' << std::endl
static int currentObjectId = 0;

class TestMap
{
    int m_id;
    int const m_objectId;
public:
    TestMap()
        : m_id( 0 )
        , m_objectId( ++ currentObjectId )
    {
        TRACE(DFLT);
    }
    TestMap( int id )
        : m_id( id )
        , m_objectId( ++ currentObjectId )
    {
        TRACE(CTOR);
    }
    TestMap( TestMap const& other )
        : m_id( other.m_id )
        , m_objectId( ++ currentObjectId )
    {
        TRACE(COPY);
    }
    ~TestMap()
    {
        TRACE(DTOR);
    }
    TestMap& operator=( TestMap const& other )
    {
        m_id = other.m_id;
        TRACE(ASGN);
        return *this;
    }
};

您可能想要添加其他信息(如m_id)到 跟踪也是如此。

另外:您的最后一个输出调用未定义的行为。后 m.find(i),你应该先检查迭代器没有 返回m.end()。如果有,则不允许取消引用。所以 你的测试输出应该是这样的:

void
testOutput( std::map<int, TestMap> const& m, int i )
{
    std::map<int, TestMap>::const_iterator entry = m.find( i );
    if ( entry == m.end() ) {
        std::cout << "no object at " << i << std::endl;
    } else {
        std::out << "object " << entry->second.m_id << " at " << i << std::endl;
    }
}

(最后:我认为微软已经抢占了C 前缀 类,所以你应该避免它。如果你想要一个前缀,选择 别的东西,以避免混淆。)

【讨论】:

    【解决方案2】:

    如果你存储一个实际的对象(而不是一个引用或指针),是的,当你删除它时,这个对象会被销毁。

    如果您存储指针或引用,则不会破坏对象,并且不会在指针上调用delete。如果您希望这种情况自动发生,您应该使用智能指针(例如 unique_ptrshared_ptr,具体取决于您想要的行为)。

    如果你不使用智能指针,那么你将需要自己存储指针,并delete对象(在使用erasemap中删除元素之后)。

    【讨论】:

      【解决方案3】:

      你的默认构造函数被第四次调用,因为m[1]

      std::cout << "erased test object: " << m[1].get_id() << std::endl;
      

      将用键“1”构造一个新对象。这是因为这样的元素在地图中还不存在——否则它只会返回那个已经存在的对象。 (它以前确实存在,但你在上面的行中删除了它!;])

      【讨论】:

        猜你喜欢
        • 2011-07-13
        • 1970-01-01
        • 1970-01-01
        • 2010-12-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-20
        • 2010-12-25
        相关资源
        最近更新 更多