2013-08-07 2 views
2

Я был любопытным, если вызван delfaut destructor, когда im удаляет элемент с карты. Вот пример, который я сделал:уничтожение объекта при удалении из 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? Я не мог найти эту информацию.
  3. Что делать, если я храню указатели на объекты (им создавая их с помощью «нового» оператора)? Когда вызывается «delete»?
+0

Создайте свой экземпляр и оператор копирования, также вы можете его распечатать. Если ваш компилятор поддерживает перемещение семантики, сделайте аналогично для своего конструктора перемещения и переместите оператор присваивания. –

+0

Возможно, вы захотите создать конструктор-копию, чтобы лучше понять это. –

+0

@MarkGarcia Даже если компилятор поддерживает семантику перемещения, он не будет использовать их, если у объекта есть пользовательский конструктор копирования. Простое определение конструктора копии достаточно для первого понимания. ('std :: map' никогда не использует назначение, и я думаю, что в C++ 11 для него было удалено Требование Assignable.) –

ответ

4

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) в следе, а

также: ваш последний output вызывает неопределенное поведение. После 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; 
    } 
} 

(наконец: Я думаю, что Microsoft имеет вытеснен префикс C для классов, поэтому следует избегать его. Если вы хотите префикс, выберите что-то еще, чтобы избежать путаницы.)

2

Если вы храните фактический объект (а не ссылку или указатель), да, объект уничтожается при его удалении.

Если вы храните указатели или ссылки, то объект не уничтожается, а delete не вызывается указателем. Если вы хотите, чтобы это произошло автоматически, вы должны использовать интеллектуальный указатель (например, unique_ptr или shared_ptr в зависимости от того, какое поведение вы хотите).

Если вы не используете смарт-указатели, то вам нужно будет принимать указатель хранится, и delete объект самостоятельно (после использования erase, чтобы удалить элемент из map).

0

Ваш конструктор по умолчанию называется четвертый раз, потому что m[1] в

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

построит новый объект с помощью ключа «1». Это связано с тем, что такой элемент еще не существует на карте - иначе он просто вернет уже существующий объект. (Это было раньше, но вы удалили его по линии выше!;))