初心者必見!Qt QList::takeAt()を使った実践コード例
QList::takeAt(int i)
は、QtのコンテナクラスであるQList
が提供する関数の一つです。この関数は、指定されたインデックスi
にある要素をリストから取り除き、その取り除いた要素のコピーを返します。
動作のポイント
-
要素の削除と返却:
takeAt()
は、指定された位置の要素をリストから削除するだけでなく、その削除された要素自体を返します。これは、単に要素を削除するremoveAt()
とは異なる重要な点です。removeAt()
は要素を削除するだけで何も返しません。 -
インデックス指定:
i
は、リスト内の要素の0から始まるインデックス(位置)を示します。例えば、takeAt(0)
はリストの最初の要素を取り除き、返します。 -
リストのサイズ変更: 要素が取り除かれるため、リストのサイズは1つ減少します。
-
効率:
QList
は内部的に動的配列として実装されているため、リストの端(先頭や末尾)からの要素の削除は非常に高速(定数時間)ですが、リストの途中からの要素の削除は、その後の要素をすべて移動させる必要があるため、要素数に比例した時間(線形時間)がかかる場合があります。
ユースケース
QList::takeAt()
は、リストから特定の要素を取り出し、その要素に対して何らかの処理を行いたい場合に非常に便利です。
例
#include <QList>
#include <QString>
#include <QDebug>
int main() {
QList<QString> list;
list << "Apple" << "Banana" << "Cherry" << "Date";
qDebug() << "Original List:" << list; // 出力: ("Apple", "Banana", "Cherry", "Date")
// インデックス1の要素("Banana")を取り出す
QString takenItem = list.takeAt(1);
qDebug() << "Taken Item:" << takenItem; // 出力: "Banana"
qDebug() << "List after takeAt():" << list; // 出力: ("Apple", "Cherry", "Date")
// 存在しないインデックスを指定するとアサーションエラーや未定義動作になる可能性があるので注意
// 例: list.takeAt(10); // 避けるべき
return 0;
}
この例では、"Banana"
がリストから取り除かれ、takenItem
変数に格納されています。元のリストからは"Banana"
がなくなっていることがわかります。
removeAt()
との違い
関数名 | 動作 | 戻り値 |
---|---|---|
takeAt(int i) | 指定されたインデックスの要素を削除し、その要素のコピーを返す。 | 削除された要素のコピー(T型) |
removeAt(int i) | 指定されたインデックスの要素を削除する。 | なし(void) |
このように、takeAt()
は要素を取り出して利用したい場合に、removeAt()
は単に要素をリストから削除したい場合に使い分けられます。
ポインタを扱う場合の注意
QList
がポインタ(例: QList<QWidget*>
) を格納している場合、takeAt()
はポインタ自体を返します。この場合、取り出したポインタが指すオブジェクトのメモリ管理はプログラマの責任となります。多くの場合、取り出したオブジェクトが不要になったらdelete
する必要があります。
QList<QWidget*> widgets;
widgets.append(new QWidget());
widgets.append(new QWidget());
QWidget* takenWidget = widgets.takeAt(0); // リストからポインタを取り出す
// takenWidget を使用する...
delete takenWidget; // 不要になったら手動でメモリを解放する
インデックス範囲外アクセス (Index Out of Bounds)
エラーの原因:
takeAt(int i)
は、i
が 0
から list.size() - 1
の範囲内にあることを想定しています。この範囲外のインデックスを指定すると、未定義動作 (Undefined Behavior) が発生し、通常はクラッシュ (セグメンテーション違反など) や予期せぬ動作につながります。
よくあるシナリオ:
- 計算されたインデックスが誤っている。
- ループ内で
takeAt()
を使用し、リストのサイズが動的に減少しているにもかかわらず、固定のインデックスまたは更新されていないサイズを使用する。 - リストが空なのに
takeAt(0)
を呼び出す。
トラブルシューティング:
- ループの注意: ループ内で要素を削除する場合、インデックスの計算方法を慎重に検討する必要があります。
- 後ろから前にループする: リストの末尾から先頭に向かって処理することで、要素の削除によるインデックスのずれを回避できます。
for (int i = list.size() - 1; i >= 0; --i) { if (condition_to_take_at_i) { MyObject obj = list.takeAt(i); // obj を処理する } }
takeFirst()
やtakeLast()
の利用: 先頭または末尾の要素を繰り返し取り出す場合は、takeFirst()
やtakeLast()
をwhile (!list.isEmpty())
と組み合わせて使用する方が安全で読みやすいです。
while (!list.isEmpty()) { MyObject obj = list.takeFirst(); // または takeLast() // obj を処理する }
- 要素を削除した際にインデックスを調整する: 前からループする場合は、要素を削除するたびにインデックスをデクリメント(
i--
)し、リストのサイズも追跡・更新する必要があります。
int i = 0; while (i < list.size()) { // size() がループごとに評価されることに注意 if (condition_to_take_at_i) { MyObject obj = list.takeAt(i); // obj を処理する // 要素を削除したので、次の要素は現在のiの位置に来るため、iを増やさない } else { i++; // 要素を削除しなかったので、次の要素に進む } }
- 必ずインデックスの範囲チェックを行う:
takeAt()
を呼び出す前に、i >= 0 && i < list.size()
の条件を確認します。
ポインタのメモリリーク (Memory Leak with Pointers)
エラーの原因:
QList<T*>
のようにポインタを格納している QList
で takeAt()
を使用する場合、takeAt()
はポインタそのものを返しますが、そのポインタが指すオブジェクトのメモリは解放しません。取り出したポインタを適切に delete
しないと、メモリリークが発生します。
よくあるシナリオ:
new
で動的に確保したオブジェクトのポインタをQList
に追加し、takeAt()
で取り出した後、delete
し忘れる。
トラブルシューティング:
QObject
の親子関係:QObject
を継承したクラスのインスタンスをリストに格納する場合、親を設定することでメモリ管理をQtに任せることもできますが、takeAt()
でリストから取り出した場合、そのオブジェクトの親が変更されない限り、親オブジェクトが破棄されるときに子オブジェクトも破棄されます。しかし、QList
から取り出したオブジェクトをすぐに使ってから破棄したい場合は、手動でdelete
するか、スマートポインタを利用するのが一般的です。- スマートポインタの利用: メモリ管理を自動化するために、
QSharedPointer
やstd::unique_ptr
などのスマートポインタを使用することを強く推奨します。これにより、手動でのdelete
が不要になり、メモリリークのリスクが大幅に減少します。#include <QList> #include <QSharedPointer> #include <QDebug> class MyObject { public: MyObject(int id) : m_id(id) { qDebug() << "MyObject" << m_id << "created"; } ~MyObject() { qDebug() << "MyObject" << m_id << "destroyed"; } int id() const { return m_id; } private: int m_id; }; int main() { QList<QSharedPointer<MyObject>> objectList; objectList.append(QSharedPointer<MyObject>(new MyObject(1))); objectList.append(QSharedPointer<MyObject>(new MyObject(2))); qDebug() << "List size before takeAt:" << objectList.size(); QSharedPointer<MyObject> takenObj = objectList.takeAt(0); // スマートポインタを取り出す qDebug() << "Taken object ID:" << takenObj->id(); qDebug() << "List size after takeAt:" << objectList.size(); // takenObj がスコープを抜けるときに自動的に delete される // または、shared_ptr の参照カウントが0になったときに delete される return 0; }
- 責任を持って
delete
する:takeAt()
でポインタを取り出したら、そのポインタが指すオブジェクトが不要になった時点で必ずdelete
してください。QList<MyObject*> objectList; objectList.append(new MyObject()); objectList.append(new MyObject()); MyObject* obj = objectList.takeAt(0); // ポインタを取り出す // obj を使用する delete obj; // オブジェクトのメモリを解放する obj = nullptr; // dangling pointer を避けるために null にする
イテレータの無効化 (Iterator Invalidation)
エラーの原因:
QList::takeAt()
はリストの構造を変更するため、そのリストに対して保持していたイテレータ(特にQList::iterator
やQList::const_iterator
)が無効になる可能性があります。無効になったイテレータを使用すると、未定義動作が発生します。
よくあるシナリオ:
QList
をイテレータでループ中にtakeAt()
を呼び出す。
トラブルシューティング:
- イテレータをループ中に使用しない、または慎重に更新する:
- 要素を削除する場合は、数値インデックスベースのループを使用し、上述の「インデックス範囲外アクセス」でのループに関する注意点に従います。
- Qt 5以降では、Qtの暗黙的共有の仕組みにより、イテレータの無効化に関するルールが複雑になっています。一般的には、リストが変更されるとイテレータが無効になると考えるのが安全です。
- Qtのドキュメントでは、
QList
のイテレータは、参照先のアイテムがコンテナに残っている限り有効であるとされていますが、コンテナのコピー中にイテレータがアクティブな場合は問題が発生する可能性があると警告されています。最も安全なのは、takeAt()
などによる変更を行う際にイテレータを再取得するか、イテレータベースの削除が必要な場合はQMutableListIterator
を使用することです。しかし、QMutableListIterator
にはtake()
のような直接的な関数はありません。
暗黙的共有 (Implicit Sharing) と予期せぬコピー (Unexpected Copy-on-Write)
エラーの原因:
QList
は暗黙的共有 (Implicit Sharing) を利用しており、リストがコピーされるときに、実際にデータが複製されるのは書き込みが行われる時(Copy-on-Write)です。takeAt()
はリストの内容を変更する操作なので、もし同じデータを持つ他の QList
のインスタンスが存在する場合、takeAt()
を呼び出すとデータの完全なコピーが発生し、パフォーマンスに影響を与える可能性があります。
トラブルシューティング:
QVector
との比較:QVector
は常に連続したメモリに要素を格納し、暗黙的共有を行わないため、パフォーマンスが予測しやすい場合があります。リストのサイズが頻繁に変わらず、インデックスアクセスが主体の場合はQVector
の利用も検討します。- パフォーマンスの考慮: 大量の要素を持つ
QList
を頻繁にコピーし、その後takeAt()
のような変更操作を行う場合、パフォーマンスにボトルネックが生じる可能性があります。
例 1: 基本的な使用法 - 特定の要素を取り出す
この例では、文字列のリストから特定のインデックスの要素を取り出し、その結果を確認します。
#include <QList>
#include <QString>
#include <QDebug> // デバッグ出力用
int main() {
// 1. QList<QString> の作成と初期化
QList<QString> fruits;
fruits << "Apple" << "Banana" << "Cherry" << "Date" << "Elderberry";
qDebug() << "--- 例 1: 基本的な使用法 ---";
qDebug() << "元のリスト: " << fruits; // 出力: ("Apple", "Banana", "Cherry", "Date", "Elderberry")
// 2. インデックス 2 (3番目の要素) の要素を取り出す
// この場合、「Cherry」が取り出される
int indexToTake = 2;
if (indexToTake >= 0 && indexToTake < fruits.size()) {
QString takenFruit = fruits.takeAt(indexToTake);
qDebug() << "取り出されたフルーツ: " << takenFruit; // 出力: "Cherry"
qDebug() << "takeAt()後のリスト: " << fruits; // 出力: ("Apple", "Banana", "Date", "Elderberry")
} else {
qDebug() << "指定されたインデックスは範囲外です。";
}
// 3. リストの最初の要素を取り出す (インデックス 0)
if (!fruits.isEmpty()) {
QString firstFruit = fruits.takeAt(0); // または fruits.takeFirst();
qDebug() << "取り出された最初のフルーツ: " << firstFruit; // 出力: "Apple"
qDebug() << "takeAt(0)後のリスト: " << fruits; // 出力: ("Banana", "Date", "Elderberry")
}
// 4. リストの最後の要素を取り出す (fruits.size() - 1)
if (!fruits.isEmpty()) {
QString lastFruit = fruits.takeAt(fruits.size() - 1); // または fruits.takeLast();
qDebug() << "取り出された最後のフルーツ: " << lastFruit; // 出力: "Elderberry"
qDebug() << "takeAt(last)後のリスト: " << fruits; // 出力: ("Banana", "Date")
}
return 0;
}
例 2: ポインタを格納する QList とメモリ管理
QList
が動的に確保されたオブジェクトへのポインタを格納している場合、takeAt()
でポインタを取り出した後のメモリ管理が重要になります。
#include <QList>
#include <QDebug>
#include <memory> // std::unique_ptr を使用するため
// 例として使用するシンプルなクラス
class MyObject {
public:
int id;
MyObject(int _id) : id(_id) {
qDebug() << "MyObject" << id << "が作成されました。";
}
~MyObject() {
qDebug() << "MyObject" << id << "が破棄されました。";
}
};
int main() {
qDebug() << "--- 例 2: ポインタとメモリ管理 ---";
// 1. 生ポインタを格納する QList (メモリリークのリスクあり)
qDebug() << "\n--- 生ポインタの例 (注意が必要) ---";
QList<MyObject*> rawPointerList;
rawPointerList.append(new MyObject(1));
rawPointerList.append(new MyObject(2));
rawPointerList.append(new MyObject(3));
qDebug() << "元の生ポインタリストのサイズ: " << rawPointerList.size(); // 出力: 3
MyObject* obj1 = rawPointerList.takeAt(0); // ポインタを取り出す
qDebug() << "取り出された MyObject ID: " << obj1->id; // 出力: 1
qDebug() << "takeAt()後の生ポインタリストのサイズ: " << rawPointerList.size(); // 出力: 2
// !!! 重要: 取り出した MyObject のメモリを解放しないとメモリリークが発生します !!!
delete obj1; // メモリを解放
// 残りの要素も手動で解放する必要がある
qDeleteAll(rawPointerList); // QList<T*> の全ての要素を delete するヘルパー関数
rawPointerList.clear(); // リストを空にする
qDebug() << "生ポインタリストの最終サイズ: " << rawPointerList.size(); // 出力: 0
// 2. スマートポインタ (QSharedPointer) を格納する QList (推奨)
// メモリ管理が自動化され、メモリリークのリスクが大幅に減少します。
qDebug() << "\n--- QSharedPointer の例 (推奨) ---";
QList<QSharedPointer<MyObject>> sharedPointerList;
sharedPointerList.append(QSharedPointer<MyObject>(new MyObject(4)));
sharedPointerList.append(QSharedPointer<MyObject>(new MyObject(5)));
sharedPointerList.append(QSharedPointer<MyObject>(new MyObject(6)));
qDebug() << "元の共有ポインタリストのサイズ: " << sharedPointerList.size(); // 出力: 3
QSharedPointer<MyObject> obj4 = sharedPointerList.takeAt(0); // スマートポインタを取り出す
qDebug() << "取り出された MyObject ID: " << obj4->id; // 出力: 4
qDebug() << "takeAt()後の共有ポインタリストのサイズ: " << sharedPointerList.size(); // 出力: 2
// obj4 がスコープを抜けるとき、または参照カウントが0になったときに MyObject(4) は自動的に破棄される
// 明示的な delete は不要
qDebug() << "sharedPointerList の残りの要素がスコープを抜けるのを待つ...";
// main() 関数の終了時に sharedPointerList が破棄され、残りの MyObject(5) と MyObject(6) も自動的に破棄される
return 0;
}
例 3: ループ内での takeAt()
の使用 (注意が必要)
ループ内で takeAt()
を使用する場合、インデックスの調整に注意が必要です。リストのサイズが動的に変化するため、単純にインデックスをインクリメントしていくと問題が発生します。
#include <QList>
#include <QString>
#include <QDebug>
int main() {
qDebug() << "--- 例 3: ループ内での takeAt() (注意が必要) ---";
QList<QString> tasks;
tasks << "Task A" << "Task B" << "Task C" << "Task D" << "Task E";
qDebug() << "元のタスクリスト: " << tasks;
// 1. 後ろから前にループするアプローチ (推奨)
// 要素を削除しても、まだ処理していない要素のインデックスには影響しないため安全
qDebug() << "\n--- 後ろから前にループするアプローチ ---";
QList<QString> tasksToProcess = tasks; // コピーを作成して元のリストはそのままにする
QList<QString> processedTasksBackward;
for (int i = tasksToProcess.size() - 1; i >= 0; --i) {
if (tasksToProcess.at(i).contains("C") || tasksToProcess.at(i).contains("A")) {
QString task = tasksToProcess.takeAt(i);
processedTasksBackward.append(task);
qDebug() << "処理されたタスク (後ろから): " << task;
}
}
qDebug() << "残りのタスク (後ろから): " << tasksToProcess;
qDebug() << "処理済みタスク (後ろから): " << processedTasksBackward;
// 2. 前から後ろにループするアプローチ (インデックス調整が必要)
// 要素を削除すると、その後の要素のインデックスが1つ前にずれるため、i をデクリメントする必要がある
qDebug() << "\n--- 前から後ろにループするアプローチ (インデックス調整あり) ---";
QList<QString> tasksToProcessForward = tasks; // 新しいコピー
QList<QString> processedTasksForward;
int i = 0;
while (i < tasksToProcessForward.size()) { // size() はループごとに再評価される
if (tasksToProcessForward.at(i).contains("B") || tasksToProcessForward.at(i).contains("D")) {
QString task = tasksToProcessForward.takeAt(i);
processedTasksForward.append(task);
qDebug() << "処理されたタスク (前から): " << task;
// 要素を削除したので、i をインクリメントすると次の要素をスキップしてしまう。
// 削除された位置に次の要素が来たので、i はそのままにしておく。
} else {
i++; // 要素を削除しなかったので、次の要素に進む
}
}
qDebug() << "残りのタスク (前から): " << tasksToProcessForward;
qDebug() << "処理済みタスク (前から): " << processedTasksForward;
return 0;
}
例 4: takeAt()
と removeAt()
の比較
takeAt()
と removeAt()
の違いを明確にする例です。
#include <QList>
#include <QString>
#include <QDebug>
int main() {
qDebug() << "--- 例 4: takeAt() と removeAt() の比較 ---";
QList<QString> listTake = {"One", "Two", "Three", "Four"};
QList<QString> listRemove = {"One", "Two", "Three", "Four"};
qDebug() << "元のリスト (take): " << listTake;
qDebug() << "元のリスト (remove): " << listRemove;
// takeAt() の使用
QString takenItem = listTake.takeAt(1); // "Two" を取り出す
qDebug() << "takeAt()で取り出されたアイテム: " << takenItem;
qDebug() << "takeAt()後のリスト: " << listTake; // 出力: ("One", "Three", "Four")
// removeAt() の使用
listRemove.removeAt(1); // "Two" を削除する (何も返さない)
// QString removedItem = listRemove.removeAt(1); // これはコンパイルエラーになる (void を代入できない)
qDebug() << "removeAt()後のリスト: " << listRemove; // 出力: ("One", "Three", "Four")
return 0;
}
QList::takeAt()
の主な目的は、「リストから特定の要素を削除し、その削除された要素を返す」ことです。この目的を達成するための代替手段や、似たような状況で考慮すべき他の方法を以下に示します。
要素を削除するだけで値は不要な場合: QList::removeAt()
最も直接的な代替方法です。もし、要素をリストから削除するだけで、その削除された要素の値自体は必要ない場合、removeAt()
が適切です。
特徴:
takeAt()
と同様に、インデックス範囲外アクセスには注意が必要です。void
を返すため、削除された要素の値は取得できません。- 指定されたインデックスの要素を削除します。
使用例:
#include <QList>
#include <QString>
#include <QDebug>
int main() {
QList<QString> items = {"Alpha", "Beta", "Gamma", "Delta"};
qDebug() << "元のリスト: " << items; // ("Alpha", "Beta", "Gamma", "Delta")
int indexToRemove = 1; // "Beta" を削除
if (indexToRemove >= 0 && indexToRemove < items.size()) {
items.removeAt(indexToRemove);
qDebug() << "removeAt()後のリスト: " << items; // ("Alpha", "Gamma", "Delta")
} else {
qDebug() << "インデックスが範囲外です。";
}
return 0;
}
先頭または末尾の要素を取り出す場合: QList::takeFirst() / QList::takeLast()
リストの先頭または末尾の要素を取り出したい場合は、takeAt(0)
や takeAt(list.size() - 1)
を使う代わりに、より読みやすく効率的な takeFirst()
や takeLast()
を使用できます。
特徴:
takeAt()
と異なり、引数なしで呼び出せるため、コードが簡潔になります。- リストが空の場合に呼び出すと未定義動作になります (通常はクラッシュ)。
- リストの先頭 (
takeFirst()
) または末尾 (takeLast()
) の要素を取り出し、返します。
使用例:
#include <QList>
#include <QString>
#include <QDebug>
int main() {
QList<QString> queue = {"Job1", "Job2", "Job3"};
qDebug() << "元のキュー: " << queue;
// キューの先頭から要素を順に処理
while (!queue.isEmpty()) {
QString job = queue.takeFirst(); // takeAt(0) の代替
qDebug() << "処理中のジョブ: " << job;
qDebug() << "残り: " << queue;
}
// 出力:
// 処理中のジョブ: "Job1"
// 残り: ("Job2", "Job3")
// 処理中のジョブ: "Job2"
// 残り: ("Job3")
// 処理中のジョブ: "Job3"
// 残り: ()
QList<int> stack = {10, 20, 30};
qDebug() << "\n元のスタック: " << stack;
// スタックの末尾から要素を順に取り出す
while (!stack.isEmpty()) {
int value = stack.takeLast(); // takeAt(stack.size() - 1) の代替
qDebug() << "取り出された値: " << value;
qDebug() << "残り: " << stack;
}
// 出力:
// 取り出された値: 30
// 残り: (10, 20)
// 取り出された値: 20
// 残り: (10)
// 取り出された値: 10
// 残り: ()
return 0;
}
値に基づいて要素を削除する場合: QList::removeOne() / QList::removeAll()
特定のインデックスではなく、リスト内の特定の値を持つ要素を削除したい場合は、これらの関数が便利です。
特徴:
- これらの関数は、削除された要素の数を返しますが、要素自体は返しません。
removeAll(const T &value)
: 指定されたvalue
の全ての出現箇所を削除します。removeOne(const T &value)
: 指定されたvalue
の最初の出現箇所を削除します。
使用例:
#include <QList>
#include <QString>
#include <QDebug>
int main() {
QList<QString> items = {"Apple", "Banana", "Orange", "Apple", "Grape"};
qDebug() << "元のリスト: " << items;
// "Apple" の最初の出現を削除
if (items.removeOne("Apple")) { // 削除に成功したかチェックできる
qDebug() << "removeOne(\"Apple\")後: " << items; // ("Banana", "Orange", "Apple", "Grape")
}
// "Apple" の全ての出現を削除
int removedCount = items.removeAll("Apple");
qDebug() << "removeAll(\"Apple\")で削除された数: " << removedCount; // 出力: 1
qDebug() << "removeAll()後のリスト: " << items; // ("Banana", "Orange", "Grape")
// 存在しない要素を削除しようとする
if (!items.removeOne("Kiwi")) {
qDebug() << "\"Kiwi\"は存在しませんでした。";
}
return 0;
}
イテレータを使用して要素を削除する場合: QList::erase() と QMutableListIterator
特定の条件を満たす複数の要素をループ中に削除したい場合、インデックスの調整は複雑になりがちです。そのような場合は、イテレータを使用するのがより安全でQtらしい方法です。
QMutableListIterator
: リストを順方向または逆方向にトラバースしながら要素を追加、削除、または置換できる、Javaスタイルのイテレータです。QList::erase(iterator pos)
: 指定されたイテレータが指す要素を削除し、削除された要素の次の要素を指すイテレータを返します。
使用例 (QList::erase):
#include <QList>
#include <QString>
#include <QDebug>
int main() {
QList<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
qDebug() << "元のリスト: " << numbers;
// 奇数を削除
auto it = numbers.begin();
while (it != numbers.end()) {
if (*it % 2 != 0) { // 奇数ならば
// erase() は削除後の次の要素を指すイテレータを返すので、それを新しい it とする
it = numbers.erase(it);
} else {
++it; // 偶数ならば次の要素に進む
}
}
qDebug() << "奇数削除後のリスト: " << numbers; // (2, 4, 6, 8, 10)
return 0;
}
使用例 (QMutableListIterator):
#include <QList>
#include <QMutableListIterator>
#include <QString>
#include <QDebug>
int main() {
QList<QString> items = {"Red", "Green", "Blue", "Yellow", "Red", "Cyan"};
qDebug() << "元のリスト: " << items;
QMutableListIterator<QString> i(items);
while (i.hasNext()) {
if (i.next() == "Red") {
// 現在の要素を削除
i.remove();
}
}
qDebug() << "remove()後のリスト: " << items; // ("Green", "Blue", "Yellow", "Cyan")
return 0;
}
要素をフィルタリングして新しいリストを作成する場合
元のリストから特定の条件に合う要素だけを「取り出して」新しいリストを構成したい場合、takeAt()
で個別に削除するよりも、フィルタリングのアプローチの方がコードが読みやすく、効率的な場合があります。
特徴:
std::remove_if
とQList::erase
を組み合わせる STL スタイルの方法もあります。- C++11以降のラムダ式と組み合わせることで、非常に柔軟なフィルタリングが可能です。
- 元のリストは変更されず、新しいリストが作成されます。
使用例 (手動フィルタリング):
#include <QList>
#include <QString>
#include <QDebug>
int main() {
QList<int> allNumbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
QList<int> evenNumbers; // 偶数だけを格納する新しいリスト
qDebug() << "元のリスト: " << allNumbers;
for (int num : allNumbers) {
if (num % 2 == 0) {
evenNumbers.append(num); // 偶数を新しいリストに追加
}
}
// この場合、allNumbers は変更されない
qDebug() << "元のリスト (変更なし): " << allNumbers;
qDebug() << "フィルタリングされた偶数: " << evenNumbers;
// もし allNumbers から偶数を取り除きたいなら、以下のようにもできる
// 処理後に残したい要素だけを新しいリストにコピーし、元のリストを置き換える
QList<int> oddNumbers;
for (int num : allNumbers) {
if (num % 2 != 0) {
oddNumbers.append(num);
}
}
allNumbers = oddNumbers; // リストを置き換える
qDebug() << "元のリストから奇数のみ残した場合: " << allNumbers; // (1, 3, 5, 7, 9)
return 0;
}
リストを一時的にクリアして再構築する場合
特徴:
- 一時的な追加リストの作成が必要になる場合がある。
- コードが簡潔になることが多い。
使用例:
#include <QList>
#include <QString>
#include <QDebug>
int main() {
QList<QString> inventory = {"Sword", "Shield", "Potion", "Sword", "Armor", "Potion"};
qDebug() << "元のインベントリ: " << inventory;
QList<QString> itemsToKeep;
for (const QString &item : inventory) {
// "Potion" 以外のアイテムだけを残す
if (item != "Potion") {
itemsToKeep.append(item);
}
}
inventory.clear(); // 全てクリア
inventory.append(itemsToKeep); // 必要なものだけ再追加
qDebug() << "Potionを除外後のインベントリ: " << inventory; // ("Sword", "Shield", "Sword", "Armor")
return 0;
}