2

当我从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,都会调用它的析构函数吗?这是 a 的一般行为 std::map吗?我找不到这个信息。
  3. 如果我正在存储指向对象的指针(我正在使用“new”运算符创建它们)怎么办?那么什么时候delete调用呢?
4

3 回答 3

6

std::map stores a copy of the object you insert. When the object is removed, it is this copy which is destructed. So after m[1] = t1;, there are two identical instances of CTestMap: t1 and the one in the map.

Also: m[1] = t1; will first create a new entry in the map, using the default constructor, and later assign t1 to it.

In general, if you want to trace instance lifetime like this, you need provide a user defined copy constructor and assignment operator which trace as well. And you probably want to output the this pointer in all of the traces. (Another technique would be to dote each object with an immutable unique identifier:

#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;
    }
};

You might want to add additional information (like m_id) to the trace as well.

Also: your last output invokes undefined behavior. After m.find(i), you should check first that the iterator hasn't returned m.end(). If it has, dereferencing isn't allowed. So your test output should be something like:

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;
    }
}

(Finally: I think Microsoft has preempted the C prefix for classes, so you should avoid it. If you want a prefix, choose something else, to avoid confusion.)

于 2013-08-07T08:58:37.517 回答
3

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

如果您存储指针或引用,则对象不会被销毁,delete也不会在指针上调用。如果您希望这种情况自动发生,您应该使用智能指针(例如unique_ptr,或者shared_ptr取决于您想要的行为)。

如果您不使用智能指针,那么您将需要存储指针,并delete自己获取对象(使用后erase从 中删除元素map)。

于 2013-08-07T08:37:28.687 回答
0

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

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

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

于 2013-08-07T08:47:10.213 回答