【Qt入門】QMap::remove()の実践コード例で学ぶデータ削除の基本
QMap
はQtが提供するジェネリックなコンテナクラスの一つで、キーと値のペア(連想配列)を格納し、キーによる高速な検索を提供します。
QMap::remove()
関数は、このQMap
から指定されたキーに一致する要素を削除するためのメソッドです。
シグネチャ
size_type QMap::remove(const Key &key)
size_type
: 削除された要素の数を返す型です。通常はint
のtypedefです。T
:QMap
の値(データ)の型を表すテンプレートパラメータです。Key
:QMap
のキーの型を表すテンプレートパラメータです。
機能
この関数は、QMap
内にkey
に一致する要素が存在する場合、その要素をマップから削除します。QMap
は同じキーを複数持つことを許可しない(QMultiMap
とは異なります)ため、指定されたkey
に対応する要素は最大で1つしかありません。
戻り値
QMap::remove()
は、削除された要素の数を返します。
key
に一致する要素が見つからず、何も削除されなかった場合は0
を返します。- もし
key
に一致する要素が見つかり、削除された場合は1
を返します。
使用例
#include <QCoreApplication>
#include <QDebug>
#include <QMap>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QMap<QString, int> ages;
// 要素の追加
ages.insert("Alice", 30);
ages.insert("Bob", 25);
ages.insert("Charlie", 35);
ages.insert("David", 25);
qDebug() << "初期状態:" << ages; // 出力例: QMap(("Alice", 30)("Bob", 25)("Charlie", 35)("David", 25))
// "Bob"を削除
int removedCount = ages.remove("Bob");
qDebug() << "Bobを削除しました。削除された数:" << removedCount; // 削除された数: 1
qDebug() << "削除後:" << ages; // 出力例: QMap(("Alice", 30)("Charlie", 35)("David", 25))
// 存在しないキーを削除しようとする
removedCount = ages.remove("Eve");
qDebug() << "Eveを削除しました。削除された数:" << removedCount; // 削除された数: 0
qDebug() << "削除後 (変更なし):" << ages; // 変更なし
// 別のキーを削除
ages.remove("Charlie");
qDebug() << "Charlieを削除後:" << ages; // 出力例: QMap(("Alice", 30)("David", 25))
return a.exec();
}
注意点
- 値のメモリ管理:
QMap
は値のコピーを保持します。もし値がポインタ型の場合、remove()
を呼び出してもポインタが指すメモリは自動的に解放されません。QMap
から要素を削除する前に、必要であればそのポインタが指すオブジェクトをdelete
するなどして手動で解放する必要があります。ただし、QSharedPointer
などのスマートポインタを使用している場合は、この限りではありません。 - イテレータの無効化:
remove()
関数によって要素が削除されると、その削除された要素を指していたイテレータは無効になります。ループ内でremove()
を使用する場合は、イテレータが無効にならないように注意が必要です。QMap
をイテレートしながら複数の要素を削除したい場合は、QMap::erase()
関数を使用するか、削除対象のキーをリストに格納してからループの後に一括で削除するなどの方法を検討してください。あるいは、Qt 5から導入された範囲ベースforループや、Qt 6.1以降で追加されたremoveIf()
関数も便利です。
QMap::remove()
は比較的シンプルですが、その使用方法やQMap
の特性を理解していないと、意図しない挙動やバグの原因となることがあります。
存在しないキーを削除しようとする
これはエラーというよりは「意図しない挙動」ですが、頻繁に発生します。
問題
remove()
は、存在しないキーを削除しようとしてもエラーを発生させません。単に0
を返します。これにより、開発者がキーが存在すると誤解している場合に、期待する要素が削除されないまま処理が進んでしまうことがあります。
例
QMap<QString, int> ages;
ages.insert("Alice", 30);
int removedCount = ages.remove("Bob"); // "Bob"は存在しない
qDebug() << "Removed count:" << removedCount; // 出力: Removed count: 0
トラブルシューティング
- QMap::contains()で事前チェック
削除する前にQMap::contains(key)
でキーの存在を確認することで、意図しない削除失敗を防ぎ、より明確なロジックを構築できます。 - 戻り値の確認
remove()
の戻り値size_type
を常に確認し、削除が成功したかどうかを判断します。
if (ages.contains("Bob")) {
ages.remove("Bob");
qDebug() << "Bob was removed.";
} else {
qDebug() << "Bob was not found in the map.";
}
イテレータの無効化 (Iterator Invalidation)
これはQMap
だけでなく、多くのコンテナで要素を削除する際に発生する最も一般的な問題です。
問題
QMap::remove()
を呼び出して要素を削除すると、その削除された要素を指していた全てのイテレータが無効になります。もし、削除後に無効になったイテレータを使用しようとすると、未定義の動作(クラッシュ、誤ったデータへのアクセスなど)を引き起こす可能性があります。
特に、QMap
をループで処理しながら要素を削除しようとすると、この問題に直面しやすいです。
誤ったコード例
QMap<QString, int> myMap;
myMap.insert("A", 1);
myMap.insert("B", 2);
myMap.insert("C", 3);
// これはおそらくクラッシュするか、意図しない動作を引き起こす
for (QMap<QString, int>::iterator it = myMap.begin(); it != myMap.end(); ++it) {
if (it.value() == 2) {
myMap.remove(it.key()); // ここで 'it' が無効になる
}
// ここで無効になった 'it' を使って ++it しようとすると問題が発生
}
トラブルシューティング
-
Qt 6.1 以降の QMap::removeIf() (ラムダ関数使用)
Qt 6.1以降では、よりモダンで簡潔な方法としてremoveIf()
が導入されました。これは、指定された条件に一致する要素を全て削除します。QMap<QString, int> myMap; myMap.insert("A", 1); myMap.insert("B", 2); myMap.insert("C", 3); myMap.removeIf([](const QString &key, int value){ Q_UNUSED(key); // キーが不要な場合はQ_UNUSEDを使用 return value == 2; }); qDebug() << myMap; // 出力例: QMap(("A", 1)("C", 3))
-
削除対象のキーを別途リストに格納
最初に削除したいキーを別のQList
などに集め、ループが終了した後にそのリストを使って一括でremove()
を呼び出す方法です。QMap<QString, int> myMap; myMap.insert("A", 1); myMap.insert("B", 2); myMap.insert("C", 3); QList<QString> keysToRemove; for (QMap<QString, int>::const_iterator it = myMap.constBegin(); it != myMap.constEnd(); ++it) { if (it.value() == 2) { keysToRemove.append(it.key()); } } for (const QString &key : keysToRemove) { myMap.remove(key); } qDebug() << myMap; // 出力例: QMap(("A", 1)("C", 3))
-
QMap::erase() の使用
QMap::erase()
は、指定されたイテレータが指す要素を削除し、次の要素を指す有効なイテレータを返します。これにより、ループ内で安全に要素を削除できます。QMap<QString, int> myMap; myMap.insert("A", 1); myMap.insert("B", 2); myMap.insert("C", 3); QMap<QString, int>::iterator it = myMap.begin(); while (it != myMap.end()) { if (it.value() == 2) { it = myMap.erase(it); // 'erase' は次の要素のイテレータを返す } else { ++it; } } qDebug() << myMap; // 出力例: QMap(("A", 1)("C", 3))
値がポインタである場合のメモリリーク
問題
QMap
にポインタ(例えばMyObject*
)を値として格納している場合、remove()
を呼び出しても、そのポインタが指すメモリは自動的に解放されません。単にQMap
からポインタ値が削除されるだけで、実際のオブジェクトはメモリ上に残り続け、メモリリークの原因となります。
誤ったコード例
class MyObject {
public:
int id;
MyObject(int i) : id(i) { qDebug() << "MyObject" << id << "created"; }
~MyObject() { qDebug() << "MyObject" << id << "destroyed"; }
};
QMap<int, MyObject*> objects;
objects.insert(1, new MyObject(100));
objects.insert(2, new MyObject(200));
objects.remove(1); // ここで new MyObject(100) が指すメモリは解放されない
// ... プログラム終了まで MyObject(100) はメモリ上に残り続ける
トラブルシューティング
-
スマートポインタの使用
最も推奨される方法は、QSharedPointer
やstd::unique_ptr
などのスマートポインタを値として使用することです。これにより、ポインタの所有権が適切に管理され、参照がなくなった時点でメモリが自動的に解放されます。#include <QSharedPointer> // ... MyObject クラスの定義 ... QMap<int, QSharedPointer<MyObject>> objects; objects.insert(1, QSharedPointer<MyObject>(new MyObject(100))); objects.insert(2, QSharedPointer<MyObject>(new MyObject(200))); objects.remove(1); // MyObject(100) の参照カウントが0になり、自動的に解放される // プログラム終了時、MyObject(200) も自動的に解放される
-
手動でのメモリ解放
remove()
を呼び出す前に、削除するポインタが指すオブジェクトを手動でdelete
します。MyObject* objToDelete = objects.value(1, nullptr); // 値を取得 if (objToDelete) { objects.remove(1); // マップから削除 delete objToDelete; // メモリを解放 }
キーの比較に関する問題
問題
QMap
はキーの比較(operator<
)を使用して要素を格納・検索します。もしカスタムクラスをキーとして使用する場合、operator<
が正しく実装されていないと、remove()
が期待通りに要素を見つけられなかったり、マップの内部状態が破損したりする可能性があります。
トラブルシューティング
-
qHash の実装 (カスタムキーで QHash を使う場合)
もしQMap
ではなくQHash
(ハッシュマップ)を使用している場合、カスタムキーに対してはqHash
関数とoperator==
を適切に実装する必要があります。QMap
はoperator<
を使用するためqHash
は直接関係しませんが、同様のキーに関する考慮事項として覚えておくと良いでしょう。 -
operator<の正しい実装
カスタムキー型を使用する場合は、厳密弱順序(strict weak ordering)を満たすようにoperator<
をオーバーロードする必要があります。- 非反射性 (
a < a
は常に偽) - 非対称性 (
a < b
が真ならb < a
は偽) - 推移性 (
a < b
とb < c
が真ならa < c
も真) - 等価の推移性 (もし
!(a < b)
かつ!(b < a)
ならa
とb
は同等。もしa
とb
が同等で、b
とc
が同等なら、a
とc
も同等)
- 非反射性 (
QMap::remove()
は、QMap
から特定のキーを持つ要素を削除するための基本的な関数です。ここでは、様々なシナリオでの使用例と、一般的な注意点に対する対応方法をコード例で示します。
基本的な要素の削除
最もシンプルなケースで、特定のキーを持つ要素を削除します。
#include <QCoreApplication>
#include <QDebug>
#include <QMap>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QMap<QString, int> studentScores;
studentScores.insert("Alice", 95);
studentScores.insert("Bob", 80);
studentScores.insert("Charlie", 70);
studentScores.insert("David", 90);
qDebug() << "--- 初期状態 ---";
qDebug() << studentScores; // 出力例: QMap(("Alice", 95)("Bob", 80)("Charlie", 70)("David", 90))
// キー "Bob" を持つ要素を削除
int removedCount = studentScores.remove("Bob");
qDebug() << "--- \"Bob\" を削除後 ---";
qDebug() << "削除された数:" << removedCount; // 出力: 削除された数: 1
qDebug() << studentScores; // 出力例: QMap(("Alice", 95)("Charlie", 70)("David", 90))
// 存在しないキー "Eve" を削除しようとする
removedCount = studentScores.remove("Eve");
qDebug() << "--- 存在しない \"Eve\" を削除しようとした後 ---";
qDebug() << "削除された数:" << removedCount; // 出力: 削除された数: 0
qDebug() << studentScores; // 変更なし
return a.exec();
}
解説
remove("Eve")
は、"Eve"というキーが存在しないため、何も削除せず、戻り値は0
になります。remove("Bob")
は、マップから"Bob"というキーとその値(80)を削除します。戻り値は1
になります。
ループ内で要素を削除する (安全な方法 - erase() の使用)
QMap
をイテレートしながら条件に合う要素を削除する場合、イテレータの無効化に注意が必要です。QMap::erase(iterator)
を使用するのが安全な方法です。
#include <QCoreApplication>
#include <QDebug>
#include <QMap>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QMap<QString, int> productQuantities;
productQuantities.insert("Apple", 10);
productQuantities.insert("Banana", 0); // 在庫なし
productQuantities.insert("Cherry", 5);
productQuantities.insert("Date", 0); // 在庫なし
productQuantities.insert("Elderberry", 12);
qDebug() << "--- 初期状態 ---";
qDebug() << productQuantities;
// 在庫が0の製品を削除する
QMap<QString, int>::iterator it = productQuantities.begin();
while (it != productQuantities.end()) {
if (it.value() == 0) {
qDebug() << "削除中:" << it.key();
it = productQuantities.erase(it); // eraseは次の要素のイテレータを返す
} else {
++it; // 削除しない場合は次の要素へ進む
}
}
qDebug() << "--- 在庫ゼロを削除後 ---";
qDebug() << productQuantities; // 出力例: QMap(("Apple", 10)("Cherry", 5)("Elderberry", 12))
return a.exec();
}
解説
- 削除しなかった場合は、
++it
で手動で次の要素に進めます。 erase()
は、削除された要素の次の要素を指すイテレータを返します。この戻り値をit
に再代入することで、イテレータの無効化を避け、安全にループを続行できます。it.value() == 0
の条件を満たす要素が見つかった場合、productQuantities.erase(it)
を呼び出します。while
ループとイテレータを使用しています。
ループ内で要素を削除する (代替方法 - キーリストの利用)
#include <QCoreApplication>
#include <QDebug>
#include <QMap>
#include <QList>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QMap<QString, QString> userRoles;
userRoles.insert("Admin1", "Administrator");
userRoles.insert("UserA", "Guest");
userRoles.insert("Admin2", "Administrator");
userRoles.insert("UserB", "Member");
userRoles.insert("GuestUser", "Guest");
qDebug() << "--- 初期状態 ---";
qDebug() << userRoles;
QList<QString> keysToRemove;
// 削除したいキーを収集
for (QMap<QString, QString>::const_iterator it = userRoles.constBegin(); it != userRoles.constEnd(); ++it) {
if (it.value() == "Guest") {
keysToRemove.append(it.key());
}
}
// 収集したキーをマップから削除
for (const QString &key : keysToRemove) {
userRoles.remove(key);
qDebug() << "削除されたユーザー:" << key;
}
qDebug() << "--- ゲストユーザー削除後 ---";
qDebug() << userRoles; // 出力例: QMap(("Admin1", "Administrator")("Admin2", "Administrator")("UserB", "Member"))
return a.exec();
}
解説
- 最初に、
QMap
をイテレートして"Guest"ロールを持つユーザーのキーをkeysToRemove
というQList
に格納します。このループではQMap
への変更は行われません。
ポインタ値を削除する際のメモリ管理
QMap
がポインタを値として持つ場合、remove()
を呼び出す前に、ポインタが指すメモリを手動で解放する必要があります。スマートポインタの使用が最も推奨されます。
手動でのメモリ解放
#include <QCoreApplication>
#include <QDebug>
#include <QMap>
class MyData {
public:
int id;
MyData(int i) : id(i) { qDebug() << "MyData" << id << "created."; }
~MyData() { qDebug() << "MyData" << id << "destroyed."; }
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QMap<int, MyData*> dataMap;
dataMap.insert(1, new MyData(101));
dataMap.insert(2, new MyData(102));
dataMap.insert(3, new MyData(103));
qDebug() << "--- 初期状態 ---";
// QMapの内容を表示するが、ポインタなのでアドレスが表示される
// 例: QMap((1, 0x...) (2, 0x...) (3, 0x...))
// キー2の要素を削除する前に、関連するメモリを解放
MyData* dataToDelete = dataMap.value(2, nullptr); // 値を取得
if (dataToDelete) {
dataMap.remove(2); // マップからポインタを削除
delete dataToDelete; // ポインタが指すオブジェクトを解放
qDebug() << "MyData 102 が削除されました。";
}
qDebug() << "--- キー2の要素削除後 ---";
// QMapの内容を表示
// プログラム終了時に残りのオブジェクトも解放する
qDeleteAll(dataMap); // QMap内の全てのポインタをdeleteするヘルパー関数
dataMap.clear(); // マップを空にする
return a.exec();
}
解説
- プログラム終了時に残りの要素も解放するために
qDeleteAll(dataMap)
とdataMap.clear()
を使用しています。qDeleteAll
はQMap
が保持する全てのポインタをdelete
し、その後dataMap.clear()
でマップを空にします。 remove(2)
を呼び出す前に、dataMap.value(2)
でポインタを取得し、それをdelete
しています。これを怠るとメモリリークが発生します。
スマートポインタ (QSharedPointer) の利用 (推奨)
#include <QCoreApplication>
#include <QDebug>
#include <QMap>
#include <QSharedPointer> // スマートポインタ
class MyData {
public:
int id;
MyData(int i) : id(i) { qDebug() << "MyData" << id << "created."; }
~MyData() { qDebug() << "MyData" << id << "destroyed."; }
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QMap<int, QSharedPointer<MyData>> dataMap;
dataMap.insert(1, QSharedPointer<MyData>(new MyData(101)));
dataMap.insert(2, QSharedPointer<MyData>(new MyData(102)));
dataMap.insert(3, QSharedPointer<MyData>(new MyData(103)));
qDebug() << "--- 初期状態 ---";
// QMapの内容を表示
// キー2の要素を削除 (スマートポインタなので自動でメモリ解放される)
dataMap.remove(2);
qDebug() << "MyData 102 が削除されました。"; // MyData 102 destroyed. が出力されるはず
qDebug() << "--- キー2の要素削除後 ---";
// QMapの内容を表示
// プログラム終了時、残りのオブジェクトも自動的に解放される
// (dataMapがスコープを抜けるか、明示的にclear()が呼ばれた時)
dataMap.clear(); // ここで101と103が解放される
qDebug() << "マップをクリアしました。";
return a.exec();
}
解説
- スマートポインタは、メモリ管理の複雑さを大幅に軽減し、メモリリークのリスクを減らします。
dataMap.clear()
を呼び出した際も、残りのQSharedPointer
オブジェクトの参照カウントがゼロになり、対応するMyData
オブジェクトが自動的に解放されます。dataMap.remove(2)
を呼び出すだけで、MyData(102)
オブジェクトはQSharedPointer
の参照カウントがゼロになるため、自動的にデストラクタが呼び出され、メモリが解放されます。- 値の型を
MyData*
からQSharedPointer<MyData>
に変更しています。
Qt 6.1 以降の QMap::removeIf() の使用
特定の条件を満たすすべての要素を削除する場合、Qt 6.1以降で利用可能なremoveIf()
は非常に便利です。
#include <QCoreApplication>
#include <QDebug>
#include <QMap>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QMap<QString, double> itemPrices;
itemPrices.insert("Laptop", 1200.0);
itemPrices.insert("Mouse", 25.0);
itemPrices.insert("Keyboard", 75.0);
itemPrices.insert("Monitor", 300.0);
itemPrices.insert("Webcam", 50.0);
qDebug() << "--- 初期状態 ---";
qDebug() << itemPrices;
// 価格が100ドル以下のアイテムを全て削除
qint64 removedCount = itemPrices.removeIf([](const QString &key, double value) {
Q_UNUSED(key); // ラムダ式内でkeyを使わない場合はQ_UNUSEDで警告を抑制
return value <= 100.0;
});
qDebug() << "--- 100ドル以下のアイテム削除後 ---";
qDebug() << "削除されたアイテム数:" << removedCount; // 出力: 削除されたアイテム数: 3
qDebug() << itemPrices; // 出力例: QMap(("Laptop", 1200)("Monitor", 300))
return a.exec();
}
Q_UNUSED(key);
は、ラムダ関数でkey
パラメータを使用しない場合に、コンパイラの「未使用の変数」警告を抑制するために使用します。- 戻り値は削除された要素の総数です。
- この例では、価格(
value
)が100.0
以下のアイテムをすべて削除しています。 removeIf()
はラムダ関数を引数に取ります。このラムダ関数は、各要素のキーと値を受け取り、削除すべき場合はtrue
、残すべき場合はfalse
を返します。
QMap::erase(iterator)
これは、特定のイテレータが指す要素を削除する最も一般的な代替手段です。特にループ処理中に要素を削除する場合に安全で効率的です。
- コード例
#include <QCoreApplication> #include <QDebug> #include <QMap> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QMap<QString, int> studentScores; studentScores.insert("Alice", 95); studentScores.insert("Bob", 80); studentScores.insert("Charlie", 70); studentScores.insert("David", 90); qDebug() << "初期状態:" << studentScores; // スコアが80以下の生徒を削除 QMap<QString, int>::iterator it = studentScores.begin(); while (it != studentScores.end()) { if (it.value() <= 80) { qDebug() << "削除中 (erase):" << it.key() << ":" << it.value(); it = studentScores.erase(it); // 削除し、次のイテレータを取得 } else { ++it; // 削除しない場合は次の要素へ } } qDebug() << "削除後:" << studentScores; // 出力例: QMap(("Alice", 95)("David", 90)) return a.exec(); }
- 特徴
- 削除された要素の次の要素を指す新しいイテレータを返します。これにより、ループ内で安全にイテレータを進めることができます。
- 戻り値は
QMap::iterator
またはQMap::const_iterator
です。
- 用途
- イテレータを使って
QMap
を走査しながら、条件に合う要素を削除したい場合。 remove(key)
と異なり、キーによる検索オーバーヘッドがないため、イテレータがすでに手元にある場合は効率的です。
- イテレータを使って
QMap::removeIf(Predicate) (Qt 6.1 以降)
Qt 6.1 から導入された新しいメソッドで、特定の条件(ラムダ関数などで定義)を満たすすべての要素を削除します。非常に簡潔で、現代的なC++のスタイルに適しています。
- コード例
#include <QCoreApplication> #include <QDebug> #include <QMap> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QMap<QString, int> productStock; productStock.insert("A", 10); productStock.insert("B", 0); productStock.insert("C", 5); productStock.insert("D", 0); productStock.insert("E", 12); qDebug() << "初期状態:" << productStock; // 在庫が0の製品をすべて削除 qint64 removedCount = productStock.removeIf([](const QString &key, int value) { Q_UNUSED(key); // キーを使用しない場合は警告を抑制 return value == 0; }); qDebug() << "削除された製品数:" << removedCount; // 出力: 削除された製品数: 2 qDebug() << "削除後:" << productStock; // 出力例: QMap(("A", 10)("C", 5)("E", 12)) return a.exec(); }
- 特徴
- ラムダ関数(または関数オブジェクト)を引数にとり、キーと値に基づいて削除するかどうかを決定します。
- 削除された要素の数を
qint64
型で返します。
- 用途
- 複雑な条件に基づいて複数の要素を一括で削除したい場合。
- ループを自分で書く手間を省きたい場合。
QMap::clear()
マップ内のすべての要素を削除し、マップを空にします。
- コード例
#include <QCoreApplication> #include <QDebug> #include <QMap> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QMap<int, QString> myMap; myMap.insert(1, "One"); myMap.insert(2, "Two"); myMap.insert(3, "Three"); qDebug() << "初期状態:" << myMap; // 出力例: QMap((1, "One")(2, "Two")(3, "Three")) myMap.clear(); // すべての要素を削除 qDebug() << "クリア後:" << myMap; // 出力: QMap() qDebug() << "マップは空か:" << myMap.isEmpty(); // 出力: マップは空か: true return a.exec(); }
- 特徴
- マップ内のすべての要素を削除し、マップのサイズを
0
にします。 - 戻り値はありません(
void
)。 - 値がポインタ型の場合、
remove()
と同様にメモリリークに注意が必要です(後述のqDeleteAll
と組み合わせて使用)。
- マップ内のすべての要素を削除し、マップのサイズを
- 用途
- マップ全体をリセットしたい場合。
QMultiMap の remove() と remove(iterator)
QMultiMap
はQMap
のサブクラスで、同じキーに対して複数の値を格納できます。QMap
のremove()
とは挙動が異なります。
- コード例 (QMultiMap の remove(key))
#include <QCoreApplication> #include <QDebug> #include <QMultiMap> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QMultiMap<QString, QString> dictionary; dictionary.insert("apple", "リンゴ"); dictionary.insert("apple", "アップル"); // 同じキー dictionary.insert("banana", "バナナ"); dictionary.insert("apple", "林檎"); // さらに同じキー qDebug() << "初期状態:" << dictionary; // 出力例: QMultiMap(("apple", "リンゴ")("apple", "アップル")("apple", "林檎")("banana", "バナナ")) // キー "apple" に対応するすべての要素を削除 int removedCount = dictionary.remove("apple"); qDebug() << "削除された数:" << removedCount; // 出力: 削除された数: 3 qDebug() << "削除後:" << dictionary; // 出力例: QMultiMap(("banana", "バナナ")) return a.exec(); }
- iterator QMultiMap::remove(iterator pos)
QMap::erase()
と同様に、指定されたイテレータが指す単一の要素を削除し、次の要素を指すイテレータを返します。QMultiMap
の特性上、同じキーを持つ複数の要素を削除したい場合、remove(const Key &key)
の方が効率的です。
- size_type QMultiMap::remove(const Key &key)
- 指定されたキーに一致するすべての要素を削除し、削除された要素の数を返します。
qDeleteAll() との組み合わせ (ポインタ値のメモリ管理)
マップの値が動的に確保されたポインタ(例: MyObject*
)である場合、remove()
やclear()
はポインタ自体をマップから削除するだけで、ポインタが指すメモリを解放しません。メモリリークを防ぐために、qDeleteAll()
などのヘルパー関数と組み合わせて使用する必要があります。
- コード例
#include <QCoreApplication> #include <QDebug> #include <QMap> #include <QList> // qDeleteAll を利用するため class MyWidget : public QObject { Q_OBJECT // QObjectから派生させる場合 public: int id; MyWidget(int i, QObject* parent = nullptr) : QObject(parent), id(i) { qDebug() << "MyWidget" << id << "created."; } ~MyWidget() { qDebug() << "MyWidget" << id << "destroyed."; } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QMap<int, MyWidget*> widgetMap; widgetMap.insert(1, new MyWidget(101)); widgetMap.insert(2, new MyWidget(102)); widgetMap.insert(3, new MyWidget(103)); qDebug() << "--- 初期状態 ---"; // QMapの内容を表示するとポインタアドレスが表示される // 特定の要素を削除し、そのメモリも解放 MyWidget* widgetToDelete = widgetMap.value(2, nullptr); if (widgetToDelete) { widgetMap.remove(2); // マップからポインタを削除 delete widgetToDelete; // オブジェクトを解放 qDebug() << "MyWidget 102 が削除されました。"; } qDebug() << "--- 残りの要素をすべてクリアし、メモリを解放 ---"; // 残りのすべてのポインタが指すオブジェクトを解放 qDeleteAll(widgetMap.values()); // QMap::values() で QList<MyWidget*> を取得 widgetMap.clear(); // マップ自体を空にする qDebug() << "--- 全てクリア後 ---"; qDebug() << "マップは空か:" << widgetMap.isEmpty(); return a.exec(); } #include "main.moc" // QObjectを使用する場合、mocファイルを含める
- 特徴
qDeleteAll(container)
は、指定されたコンテナ内のすべてのポインタをdelete
します。qDeleteAll
を呼び出した後、clear()
を呼び出してマップ自体を空にするか、remove()
を呼び出す必要があります。
- 用途
- マップが
T*
(ポインタ)を値として格納しており、そのオブジェクトのメモリを解放したい場合。
- マップが
重要
最も安全で推奨されるのは、値として生のポインタを格納するのではなく、スマートポインタ (QSharedPointer
や std::unique_ptr
) を使用することです。これにより、上記のような手動でのメモリ管理の手間を省き、メモリリークのリスクを大幅に減らすことができます。