从std :: map删除对象时销毁对象

Jos*_*siP 2 c++ stdmap std erase

当我从中删除元素时,如果调用了默认析构函数,我感到很好奇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;
}
Run Code Online (Sandbox Code Playgroud)

这里是输出:

./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
Run Code Online (Sandbox Code Playgroud)

问题是:

  1. 当我仅使用自己的构造函数创建3个对象时,为什么要调用这么多次默认构造函数?
  2. 基于此示例,我能否说每次我从中擦除任何对象时 std::map,其析构函数都称为?这是一般行为 std::map吗?我找不到此信息。
  3. 如果我存储对象的指针(我使用“ new”运算符创建它们)怎么办?那delete什么时候才叫?

Jam*_*nze 6

std::map存储您插入的对象的副本。删除对象后,此副本将被破坏。因此,之后m[1] = t1;有两个相同的实例 CTestMapt1和映射中的一个。

同样: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;
    }
};
Run Code Online (Sandbox Code Playgroud)

您可能还希望向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;
    }
}
Run Code Online (Sandbox Code Playgroud)

(最后:我认为Microsoft已抢占了C类的前缀,因此您应该避免使用它。如果需要前缀,请选择其他选项以避免混淆。)