Почему память все еще доступна после вызова std::map::clear ()?

я наблюдаю странное поведение std::map:: clear(). Этот метод должен вызывать деструктор элемента при вызове, однако память по-прежнему доступна после вызова clear().

например:

struct A
{
  ~A() { x = 0; }
  int x;
};

int main( void )
{
  std::map< int, A * > my_map;
  A *a = new A();
  a->x = 5;
  my_map.insert( std::make_pair< int, *A >( 0, a ) );

  // addresses will be the same, will print 5
  std::cout << a << " " << my_map[0] << " " << my_map[0]->x << std::endl;

  my_map.clear();

  // will be 0
  std::cout << a->x << std::endl;

  return 0;
}

вопрос в том, почему переменная a все еще доступен после того, как его деструктор был вызван map:: clear()? Нужно ли мне писать delete a; после вызова my_map.clear() или безопасно перезаписать содержимое a?

заранее спасибо за ваша помощь, снег!--6-->

6 ответов


std:: map не управляет памятью, на которую указывают значения указателей - это зависит от вас, чтобы сделать это самостоятельно. Если вы не хотите использовать интеллектуальные указатели, вы можете написать функцию общего назначения free & clear следующим образом:

template <typename M> void FreeClear( M & amap ) 
    for ( typename M::iterator it = amap.begin(); it != amap.end(); ++it ) {
        delete it->second;
    }
    amap.clear();
}

и использовать его:

std::map< int, A * > my_map;
// populate
FreeClear( my_map )

;


Если вы храните указатели на карте (или список, или что-нибудь подобное) вы ответственны за удаление указателей, так как карта не знает, были ли они созданы с помощью new или нет. Функция clear вызывает деструкторы только в том случае, если вы не используете указатели.

О, и еще одно: вызов деструктора (или даже вызов delete) не означает, что память больше не может быть доступна. Это только означает, что вы будете получать доступ к мусору, если вы это сделаете.


потому что map.clear() вызывает деструкторы данных, содержащихся на карте, в вашем случае указателя на a. И это ничего не дает.

вы можете поставить какой-то смарт-указатель на карте для памяти, занятой a будет автоматически восстановлена.

кстати, почему вы помещаете аргументы шаблона в вызов make_pair? Дедукция аргумента шаблона должна здесь неплохо работать.


когда вы освобождаете часть памяти кучи, ее содержимое не обнуляется. Они просто снова доступны для распределения. Конечно, вы должны считать память недоступной, потому что эффекты доступа к нераспределенной памяти не определены.

фактически предотвращение доступа к странице памяти происходит на более низком уровне, и библиотеки std этого не делают.

когда вы выделяете память с Новым, вам нужно удалить ее самостоятельно, если вы не используете умный указатель.


любой контейнер хранит ваш тип объекта и вызывает соответствующие конструкторы: внутренний код каждый узел может выглядеть так:

__NodePtr
{
    *next;
    __Ty    Val;
}

когда вы выделяете, это происходит путем построения val на основе типа, а затем связывания. Что-то похожее на:

_Ty _Val = _Ty();
_Myhead = _Buynode();
_Construct_n(_Count, _Val);

при удалении он вызывает соответствующие деструкторы.

когда вы храните ссылки (указатели), он не будет вызывать конструктор и не будет разрушаться.


проведя последние 2 месяца еды, сна и дыхания карты, у меня есть рекомендация. Пусть карта выделяет свои собственные данные, когда это возможно. Это намного чище, именно по тем причинам, которые вы выделяете здесь.

есть также некоторые тонкие преимущества, например, если вы копируете данные из файла или сокета в данные карты, Хранилище данных существует, как только узел существует, потому что когда карта вызывает malloc() для выделения узла, он выделяет память для и ключ, и данные. (АКА карта[ключ].сначала и карта[ключ].секунду)

это позволяет использовать оператор присваивания вместо memcpy (), и требует 1 меньше вызова malloc () - тот, который вы делаете.

IC_CDR CDR, *pThisCDRLeafData;  // a large struct{}

    while(1 == fread(CDR, sizeof(CDR), 1, fp))  {
    if(feof(fp)) {
        printf("\nfread() failure in %s at line %i", __FILE__, __LINE__);
    }
    cdrMap[CDR.iGUID] = CDR; // no need for a malloc() and memcpy() here    
    pThisCDRLeafData  = &cdrMap[CDR.iGUID]; // pointer to tree node's data

несколько предостережений, чтобы быть в курсе, стоит отметить здесь.

  1. не вызывайте malloc () или new в строке кода, которая добавляет узел дерева, поскольку ваш вызов malloc () вернет указатель До того, как вызов карты malloc () выделит место для удержания возврата из вашего malloc ().
  2. в режиме отладки ожидайте подобных проблем при попытке освободить () вашу память. Оба они кажутся мне проблемами компилятора, но, по крайней мере, в MSVC 2012 они существуют и являются серьезной проблемой.
  3. подумайте о том, где "привязать" ваши карты. IE: где они объявлены. Ты же не хочешь, чтобы они по ошибке вышли за рамки. main{} всегда в безопасности.

    INT _tmain(INT argc, char* argv[])    {
    IC_CDR      CDR, *pThisCDRLeafData=NULL;
    CDR_MAP     cdrMap;
    CUST_MAP    custMap;
    KCI_MAP     kciMap;
    
  4. у меня было очень удачи, и я очень рад, что критическая карта выделяет структуру как данные узла, и эта структура "привязывает" карту. Хотя анонимные структуры были оставлены C++ (ужасное, ужасное решение, которое должно быть отменено), карты, которые являются 1-м членом структуры, работают так же, как анонимные структуры. Очень гладкий и чистый с нулевыми размерными эффектами. Передача указателя на структуру, принадлежащую листу, или копию структуры по значению в вызове функции, оба работают очень хорошо. Очень рекомендуемый.

  5. вы можете поймать возвращаемое значение .вставьте, чтобы определить, нашел ли он существующий узел на этом ключе или создал новый. (см. #12 для кода) использование нотации индекса не позволяет этого. Возможно, лучше остановиться на этом .вставьте и придерживайтесь его, особенно потому, что [] нотация не работает с multimaps. (это не имеет смысла делать, так как нет ключа" a", но серия Ключей с одинаковыми значениями в multimap)
  6. вы можете, и следует, также Ловушка возвращается .стирать и. empty () (да, это раздражает, что некоторые из этих вещей являются функциями и нуждаются в (), а некоторые, например .стереть, не надо)
  7. вы можете получить как значение ключа, так и значение данных для любого узла карты .первый и .во-вторых, которые все карты, по соглашению, используют для возврата ключа и данных соответственно
  8. сохранить себе огромное количество путаницы и ввода, и использовать typedefs для ваших карт, как так.

    typedef map<ULLNG, IC_CDR>      CDR_MAP;    
    typedef map<ULLNG, pIC_CDR>     CALL_MAP;   
    typedef struct {
        CALL_MAP    callMap;
        ULNG        Knt;         
        DBL         BurnRateSec; 
        DBL         DeciCents;   
        ULLNG       tThen;       
        DBL         OldKCIKey;   
    } CUST_SUM, *pCUST_SUM;
    typedef map<ULNG,CUST_SUM>  CUST_MAP, CUST_MAP;  
    typedef map<DBL,pCUST_SUM>  KCI_MAP;
    
  9. передать ссылки на карты с помощью оператора typedef и&, как в

    ULNG DestroyCustomer_callMap(CUST_SUM Summary, CDR_MAP& cdrMap, KCI_MAP& kciMap)

  10. используйте тип переменной "auto" для итераторов. Компилятор вычислит из типа, указанного в остальной части тела цикла for (), какую карту typedef использовать. Он такой чистый, почти волшебный!

    for(auto itr = Summary.callMap.begin(); itr!= Summary.callMap.end(); ++itr) {

  11. определите некоторые константы манифеста, чтобы сделать возврат из .стирать и. empty() более осмысленно.

    if(ERASE_SUCCESSFUL == cdrMap.erase (itr->second->iGUID)) {

  12. учитывая, что" умные указатели " действительно просто сохраняют счетчик ссылок, помните, что вы всегда можете сохранить свой собственный счетчик ссылок, вероятно, более чистым и более очевидным способом. Комбинируя это с #5 и #10 выше, вы можете написать хороший чистый код, как это.

    #define Pear(x,y) std::make_pair(x,y) //  some macro magic
    
    auto res = pSumStruct->callMap.insert(Pear(pCDR->iGUID,pCDR));
    if ( ! res.second ) {
        pCDR->RefKnt=2;
    } else {
        pCDR->RefKnt=1;
        pSumStruct->Knt += 1;
    }
    
  13. используя указатель, чтобы повесить на узел карты, который выделяет все для себя, т. е.: нет пользователя указатели, указывающие на объекты user malloc () ed, хорошо работают, потенциально более эффективны и используются для мутирования данных узла без побочных эффектов в моем опыте.

  14. на ту же тему такой указатель можно использовать очень эффективно для сохранения состояния узла, как в pThisCDRLeafData выше. Передача этого функции, которая мутирует / изменяет данные этого конкретного узла, чище, чем передача ссылки на карту и ключа, необходимого для возврата к узлу pThisCDRLeafData указывает на.

  15. итераторы не волшебные. Они дороги и медленны, так как вы перемещаетесь по карте, чтобы получить значения. Для карты, содержащей миллион значений, вы можете прочитать узел на основе ключа со скоростью около 20 миллионов в секунду. С итераторами это, вероятно, ~ 1000 раз медленнее.

Я думаю, что на данный момент это покрывает. Будет обновляться, если какие-либо из этих изменений или есть дополнительные идеи для обмена. Мне особенно нравится использовать STL с C-код. IE:нигде не видно класса. Они просто не имеют смысла в контексте, в котором я работаю, и это не проблема. Удача.