私はこのようなコードを持っています:
class MapIndex
{
private:
typedef std::map<std::string, MapIndex*> Container;
Container mapM;
public:
void add(std::list<std::string>& values)
{
if (values.empty()) // sanity check
return;
std::string s(*(values.begin()));
values.erase(values.begin());
if (values.empty())
return;
MapIndex *mi = mapM[s]; // <- question about this line
if (!mi)
mi = new MapIndex();
mi->add(values);
}
}
私が抱えている主な懸念は、新しいアイテムがマップに追加された場合に、mapM [s]式がNULLポインターへの参照を返すかどうかです。
SGI docs 次のように言います:data_type&operator [](const key_type&k)特定のキーに関連付けられているオブジェクトへの参照を返します。マップにそのようなオブジェクトがまだ含まれていない場合、operator []はデフォルトのオブジェクトdata_type()。を挿入します
だから、私の質問は、defaultオブジェクトdata_type()の挿入がNULLポインターを作成するのか、それともメモリのどこかを指す無効なポインターを作成する可能性があるのかということです?
NULL
(0)ポインターを作成しますが、これはとにかく無効なポインターです:)
はい、それはゼロ(NULL)ポインターである必要があります。これは、stlコンテナーが明示的に格納されていない場合(つまり、実行中にマップ内の存在しないキーにアクセスしたり、ベクトルのサイズを大きくしたりする場合)、デフォルトでオブジェクトを初期化するためです。
C++標準、8.5段落5は次のように述べています。
タイプTのオブジェクトをデフォルトで初期化するには、次のことを意味します。
- Tが非PODクラスタイプ(句クラス)の場合、Tのデフォルトコンストラクターが呼び出されます(Tにアクセス可能なデフォルトコンストラクターがない場合、初期化の形式が正しくありません)。
- Tが配列型の場合、各要素はデフォルトで初期化されます
- それ以外の場合、オブジェクトのストレージはゼロで初期化されます。
また、デフォルトの初期化は、コンストラクターを単に省略することとは異なることに注意する必要があります。コンストラクターを省略し、単純な型を宣言するだけでは、不確定な値が得られます。
int a; // not default constructed, will have random data
int b = int(); // will be initialised to zero
PDATE:プログラムを完了しましたが、その行が原因でクラッシュすることがありますが、後の段階です。問題は、std :: mapに格納されているポインタを変更せずに新しいオブジェクトを作成していることです。本当に必要なのは、そのポインターへの参照またはポインターです。
MapIndex *mi = mapM[s]; // <- question about this line
if (!mi)
mi = new MapIndex();
mi->add(values);
次のように変更する必要があります。
MapIndex* &mi = mapM[s]; // <- question about this line
if (!mi)
mi = new MapIndex();
mi->add(values);
誰もこれに気づかなかったのには驚きました。
式data_type()
は、デフォルトで初期化されたオブジェクトに評価されます。非PODタイプの場合、デフォルトのコンストラクターが呼び出されますが、ポインターなどのPODタイプの場合、デフォルトの初期化はゼロの初期化と同等です。
そうです、マップがNULL
ポインタを作成することを信頼できます。説明については、 疑似コンストラクタイニシャライザ を参照してください。
クラッシュについてはわかりませんが、このステートメントのように明らかにメモリリークが発生します
if(!mi)mi = new MapIndex();
ポインタmiは、sの特定の値に対してmapMが保持しているものを参照していないため、常にtrueを返します。
また、通常のポインターの使用を避け、boost :: shared_ptrまたは破棄されたときにメモリを解放するその他のポインターを使用します。これにより、mapM.clear()またはerase()を呼び出すことができ、マップに格納されているキーと値のデストラクタを呼び出す必要があります。値がポインタなどのPODの場合、マップ全体を反復処理しているときに手動で削除しない限り、デストラクタは呼び出されません。メモリリークが発生します。