2015-10-28 2 views
1

У меня есть этот код:Как деструкторы значений std :: map call value?

#include <iostream> 
#include <map> 
using namespace std; 

class test{ 
    public: 
    test() { 
    cout << "calling test ctor " << endl; 
    } 

    ~test() { 
     cout << "calling test dtor " << endl; 
    } 

    void callme(){ 
     cout << "call me " << endl; 
    } 
}; 

int main() { 
    map<int, test> mp; 
    mp[0].callme(); 
    mp[0].callme(); 
    return 0; 
} 

Вывод этой программы является:

calling test ctor 
calling test dtor 
calling test dtor 
call me 
call me 
calling test dtor 

Я нахожусь немного запутался, как станд :: карта обработки теста :: ctors и dtors здесь.

Перед выполнением этого кода, мое предположение было, что тр [0] .callme() будет создавать новый объект test и вызвать callme() по этому поводу, и если мы называем тр [0] .callme() еще раз, то он должен call test :: dtor (поскольку мы заменяем ключ 0 здесь), а затем test :: ctor, чтобы создать новый объект test, чтобы он мог вызвать callme(). Очевидно, мое предположение здесь неверно, потому что вывод не совпадает вообще.

Не мог бы кто-нибудь пролить свет на это?

EDIT1:

НКУ --version = ССАГПЗ (GCC) 5.1.1 20150422 (Red Hat 5.1.1-1)

Команда компиляции:

g++ maps.cpp

Итак, никаких флагов с g ++. Простая компиляция.

+6

Вы забыли указать конструктор копирования. –

+1

Я не уверен, как вы получили этот результат. запуск кода [здесь] (http://coliru.stacked-crooked.com/a/8e05917741e4afef) дает правильный результат. – NathanOliver

+0

Назовите компилятор и версию. – 101010

ответ

0

Видимо, ваш operator[] использует следующую логику:

  1. Если объект не существует, создайте его, а затем установить его равным объект по умолчанию возведенных.

  2. Верните ссылку на объект.

Это очень странное поведение. Дополнительная конструкция и назначение не нужны. В первую очередь новый объект должен быть сконфигурирован по умолчанию.

0

Для полноты:

#include <iostream> 
#include <map> 
using namespace std; 

class test{ 
    public: 
    test() { 
    cout << "calling test ctor " << endl; 
    } 

    test(const test&) { 
    cout << "calling copy ctor " << endl; 
    } 

// test(test&&) { 
//  cout << "calling move ctor " << endl; 
// } 

    test& operator = (const test&) { 
    cout << "calling copy assignment " << endl; 
    return * this; 
    } 

// test& operator = (test&&) { 
//  cout << "calling move assignment " << endl; 
//  return * this; 
// } 

    ~test() { 
     cout << "calling test dtor " << endl; 
    } 

    void callme(){ 
     cout << "call me " << endl; 
    } 
}; 

int main() { 
    map<int, test> mp; 
    mp[0].callme(); 
    mp[0].callme(); 
    return 0; 
} 

Собраный C++ 11 дает:

calling test ctor 
calling copy ctor 
calling copy ctor 
calling test dtor 
calling test dtor 
call me 
call me 
calling test dtor 

Возникли комментарии по ходу семантики удаленных и скомпилированных с C++ 11:

calling test ctor 
call me 
call me 
calling test dtor 

Оба скомпилированы с g ++ 4.8.4

+0

Более интересный вывод может быть получен, если строки cout также распечатывают адрес объектов 'test'. Затем вы можете увидеть, что в режиме no-C++ 11 он создает «тест» в стеке, копирует его в другом месте в стеке и затем копирует его в кучу, прежде чем уничтожить две копии стека. Все это происходит в реализации 'std :: map :: operator []' и даже с '-O3', что кажется несколько сломанным. –

1

Скомпилировав команду g++ maps.cpp, вы вызываете g ++ в режиме C++ 03, что означает, что он не может использовать семантику перемещения.

Соответствующие линии map::operator[] реализации можно найти here

if (__i == end() || key_comp()(__k, (*__i).first)) 
#if __cplusplus >= 201103L 
     __i = _M_t._M_emplace_hint_unique(__i, std::piecewise_construct, 
         std::tuple<const key_type&>(__k), 
         std::tuple<>()); 
#else 
     __i = insert(__i, value_type(__k, mapped_type())); 
#endif 

Так перед C++ 11, в mapped_type (test в вашем примере) по умолчанию строится для создания value_type (pair<int const, test> в вашем примере). Это начальный вызов конструктора.

Звонок insert затем должен скопировать отображаемый тип не реже одного раза, когда он вставляет его во внутреннее хранилище для map. Очевидно, что реализация libstdC++ приводит к дополнительной копии где-то, добавляя до двух конструкций копирования и, следовательно, двух соответствующих деструкторных вызовов. Если вы добавите определение конструктора копии, вы увидите, что количество вызовов деструктора соответствует количеству вызовов конструктора.

Live demo

Кроме того, обратите внимание, что при добавлении -std=c++11 флага, избежать промежуточных копий. Как видно из приведенного выше кода, реализация использует кусочную конструкцию pair в этом случае, чтобы непосредственно построить mapped_typekey_type) в внутренней памяти map.

Смежные вопросы