ポインタ要素のmap

4195 ワード

map(multimap)コンテナは関連式コンテナであり、プログラミングでよく使われるコンテナであり、キー値(key)と実値(value)があり、辞書、マッピングテーブルとも呼ばれる.
次のコードに何か問題があるかわかりますか?
例1:
#pragma warning (disable : 4786)

#include 

#include 

using namespace std;

void main() {

      map< int, int* > mapInt;

      for ( int i = 0; i < 5; i++ ) {

             mapInt[ i ] = new int( i );

      }

      //           2   

      mapInt[ 2  ] = new int( 2 );

      

      //          

      //         。

      map< int, int* >::iterator itMap = mapInt.begin();

      for ( ; itMap != mapInt.end();  ++itMap ) {

             delete itMap->second;

      }

}

例2:
void main() {



      map< int, int* > mapInt;

      for ( int i = 0; i < 5; i++ ) {

             mapInt.insert( make_pair( i, new int( i ) ) );

      }

      //           2   

      mapInt.insert( make_pair( 2, new int( 2 ) ) );

      

      //          

      //         。

      map< int, int* >::iterator itMap = mapInt.begin();

      for ( ; itMap != mapInt.end();  ++itMap ) {

             delete itMap->second;

      }

}

例3:
void main() {

      map< int, int > mapInt;

      for ( int i = 0; i < 5; i++ ) {

             mapInt.insert( make_pair( i,  i ) );

      }

      

      mapInt.insert( make_pair( 5,  2 ) );

      

      //            2   

      map< int, int >::iterator itMap = mapInt.begin();

      for ( ; itMap != mapInt.end();  ++itMap ) {

             if (  itMap->second == 2 ) {

                    mapInt.erase( itMap );

             }

      }

}

分析:
      例1はmapInt[2]のためメモリが漏洩する ] = 新int(2);この文は元のキー値が2の要素の実値ポインタを上書きし、元のポインタが野ポインタとなり、メモリが漏れる.
例2では、mapInt.insert(make_pair(2,new int(2))のメモリ漏洩も発生します.この文は、キー値が2の要素がすでに存在し、挿入要素が失敗しているため、割り当てられたばかりのメモリへのポインタが野ポインタとなり、メモリ漏洩が発生します.
mapコンテナの要素挿入方法.mapコンテナのinsertメンバー関数を呼び出して要素を挿入したり、mapコンテナの下付き演算式で直接値を付与したりすることができますが、ここでは実値がポインタの場合、重複キー値の要素を挿入するとメモリが漏洩することに注意してください.したがって、要素を挿入する場合は、挿入に成功したかどうかを確認する必要があります.
正しい方法:
void main() {

      map< int, int* > mapInt;

      bool bRet;

      for ( int i = 0; i < 5; i++ ) {

             int* pI = new int( i );

             bRet = mapInt.insert( make_pair( i, pI ) ).second;

             if ( !bRet ) {

                    //              。

                    delete pI;

             }

      }

      //           2   

      int* pI = new int( 2 );

      bRet = mapInt.insert( make_pair( 2, pI ) ).second;

      if ( !bRet ) {

             //              。

             delete pI;

      }

      

      //          

      //         。

      map< int, int* >::iterator itMap = mapInt.begin();

      for ( ; itMap != mapInt.end();  ++itMap ) {

             delete itMap->second;

      }

}

例3はプログラムが定義されていないエラーを引き起こし、windowsでは不正メモリにアクセスし、プログラムがドロップする.mapInt.erase(itMap);呼び出し後itMap反復器が無効になったため、++itMapを実行すると不正メモリにアクセスし、プログラムがドロップする.
erase()が常に次の要素の位置を返す場合は、vectorコンテナで要素を削除するように、次のようにします.
//            2   

      map< int, int >::iterator itMap = mapInt.begin();

      for ( ; itMap != mapInt.end();   ) {

             if (  itMap->second == 2 ) {

                    itMap = mapInt.erase( itMap ); 

             }

             else {

                    ++itMap;

             }



      }

ただし、上記の方式ではvcでP.J.STLを使用する場合のみコンパイルが通過し、SGI STLライブラリを使用する場合はコンパイルが行われることに注意する.ただし、SGISTLライブラリは、ユーザがこの特性を必要としないと性能が損なわれることを考慮して設計されているため、このような考え方は否決される.したがって、移植性を保証するには、以下の方法を採用することが望ましい.
//              2   

        map< int, int >::iterator itMap = mapInt.begin();

        for ( ; itMap != mapInt.end();  ) {

                  if (  itMap->second == 2 ) {

                           //         itMap++  itMap       ,          ,  

                           //         erase()    ,itMap           

                           mapInt.erase( itMap++ ); 

                  }

                  else {

                           ++itMap;

                  }

        }