emplace_backだけじゃない!QListの要素追加メソッドを徹底比較【Qtプログラミング】
これは Qt のコンテナクラスである QList
が提供するメンバー関数の一つです。C++11 以降で導入された「emplace_back」という概念を QList
に適用したものです。
各要素の解説
-
QList::emplace_back()
:- これは
QList
クラスのメンバー関数で、リストの末尾に新しい要素を追加する目的で使用されます。 - 通常の
append()
やpush_back()
がオブジェクトをコピーまたはムーブしてからリストに追加するのに対し、emplace_back()
は指定された引数を使用してリストの内部で直接オブジェクトを「構築 (emplace)」します。
- これは
-
QList<T>::reference
:- これは
emplace_back
関数の戻り値の型です。 QList<T>::reference
は、新しくQList
の末尾に追加された要素への参照(T&
に相当)を返します。これにより、要素が作成された直後にその要素にアクセスして操作することができます。
- これは
-
template <typename... Args>
:- これは「可変引数テンプレート (variadic template)」と呼ばれる C++11 以降の機能です。
typename... Args
は、emplace_back
関数が任意の数の、任意の型の引数を受け取ることができることを示します。これらの引数は、QList
に追加される要素のコンストラクタに直接転送されます。
emplace_back
の利点
emplace_back
を使用する主な利点は、オブジェクトの構築にかかるオーバーヘッドを削減できる点にあります。
-
完全転送 (Perfect Forwarding):
emplace_back
は、渡された引数を要素のコンストラクタに「完全転送」します。これは、引数が値渡し、左辺値参照、右辺値参照のいずれであっても、その元の型と値カテゴリを保持したまま転送されることを意味します。これにより、要素のコンストラクタが最も効率的な方法で呼び出されます。
-
コピーやムーブの削減:
QList::append(value)
やQList::push_back(value)
の場合、まずvalue
オブジェクトがどこかで作成され、その後そのオブジェクトがQList
の内部にコピー(またはムーブ)されます。emplace_back
の場合、value
オブジェクトはQList
の内部で直接構築されるため、中間的なコピーやムーブ操作が不要になり、パフォーマンスが向上する可能性があります。特に、構築コストが高い複雑なオブジェクトを扱う場合に顕著な効果を発揮します。
使用例
例えば、以下のようなカスタムクラス MyObject
があるとします。
class MyObject {
public:
MyObject(int id, const QString& name) : m_id(id), m_name(name) {
qDebug() << "MyObject constructed: " << m_name;
}
// コピーコンストラクタやムーブコンストラクタも定義されていると仮定
private:
int m_id;
QString m_name;
};
QList<MyObject>
に要素を追加する場合、
append() を使用する場合
QList<MyObject> list;
MyObject obj(1, "First"); // MyObject がここで構築される
list.append(obj); // obj が QList にコピーされる (またはムーブされる)
emplace_back() を使用する場合
QList<MyObject> list;
list.emplace_back(2, "Second"); // MyObject が QList の内部で直接構築される
この例では、emplace_back
を使用することで、MyObject
が一度だけ構築されるのに対し、append
の場合は一度オブジェクトが構築された後、さらにコピー(またはムーブ)の操作が発生する可能性があります。
コンパイルエラー
エラーの種類
-
- エラーメッセージの例:
no matching function for call to 'MyObject::MyObject(int)'
no matching constructor for initialization of 'T'
error: no matching function for call to 'QList<MyClass>::emplace_back(const char [6])'
- 原因:
emplace_back
に渡す引数は、QList
に格納する型T
のコンストラクタの引数と完全に一致している必要があります。引数の型、数、const/non-const 性、参照修飾子などが合っていない場合、コンパイラは適切なコンストラクタを見つけられずにエラーとなります。 - トラブルシューティング:
T
クラスに、emplace_back
に渡した引数に対応するコンストラクタが定義されているか確認してください。- 引数の型が正確に一致しているか、暗黙の型変換に頼りすぎていないか確認してください。例えば、
QString
を期待しているところにconst char*
を渡す場合、QString
にconst char*
を受け取るコンストラクタがあるか確認が必要です。 - コピーコンストラクタやムーブコンストラクタ、あるいはデフォルトコンストラクタがないために問題が発生している可能性もあります(特に、他のコンストラクタを定義した場合、コンパイラが自動生成しないことがあるため)。
- エラーメッセージの例:
-
emplace_back
が存在しない(古いQtバージョン、または間違ったヘッダー)- エラーメッセージの例:
'class QList<T>' has no member named 'emplace_back'
- 原因:
emplace_back
は C++11 以降で導入された機能であり、Qt のバージョンによっては提供されていない場合があります。また、必要なヘッダーファイルがインクルードされていない可能性もあります。 - トラブルシューティング:
- 使用している Qt のバージョンを確認し、
emplace_back
がサポートされているか公式ドキュメントで確認してください。通常、Qt 5 以降でサポートされています。 QList
を使用しているファイルで<QList>
ヘッダーが正しくインクルードされているか確認してください。- プロジェクトの
.pro
ファイルでCONFIG += c++11
(またはc++14
,c++17
など) が設定されていることを確認し、C++11 以降の標準が有効になっていることを確認してください。
- 使用している Qt のバージョンを確認し、
- エラーメッセージの例:
-
非コピー可能/非ムーブ可能な型を
QList
に格納しようとしている- 原因:
QList
は内部的に要素をコピーまたはムーブすることがあるため、格納する型T
はコピー可能 (CopyConstructible) またはムーブ可能 (MoveConstructible) である必要があります。emplace_back
は直接構築を行いますが、QList
の内部的な実装(要素の再配置など)によっては、それでもコピーやムーブが要求される場合があります。 - トラブルシューティング:
- 格納する型
T
が、コピーコンストラクタとムーブコンストラクタを持っていることを確認してください。 - RAII (Resource Acquisition Is Initialization) オブジェクトなど、厳密にムーブセマンティクスを要求する型を
QList
に格納する場合は、QVector
やstd::vector
の方が適している場合があります。QList
の内部実装はQVector
とは異なり、要素が連続したメモリに配置されないため、一部の最適化が適用されにくい場合があります。
- 格納する型
- 原因:
実行時エラー / 想定外の動作
-
構築されたオブジェクトの状態が不正
- 原因:
emplace_back
に渡す引数が、要素のコンストラクタにとって無効な値であったり、初期化に失敗する可能性のある値である場合。 - トラブルシューティング:
emplace_back
に渡している引数が、格納されるオブジェクトのコンストラクタにとって有効な範囲の値か、期待される型と一致しているかデバッグして確認してください。- 特にポインタや参照を渡す場合、それらが有効なオブジェクトを指しているか確認してください。ダングリングポインタやヌルポインタを渡すと、未定義動作を引き起こす可能性があります。
- 原因:
-
メモリリーク (特にポインタを格納する場合)
- 原因:
QList<MyObject*>
のようにポインタを格納し、emplace_back
でポインタを追加した場合、QList
が破棄されても、指し示されているオブジェクト自体は破棄されません。手動でメモリを解放しないとメモリリークが発生します。 - トラブルシューティング:
QList<MyObject*>
を使用する場合は、リストが破棄される前に、格納されているすべてのポインタが指すオブジェクトをdelete
する責任があります。- スマートポインタ (
QSharedPointer
,std::shared_ptr
,std::unique_ptr
など) を使用することを強く推奨します。例えば、QList<QSharedPointer<MyObject>>
とすれば、自動的にメモリ管理が行われます。
- 原因:
-
スレッドセーフティの問題
- 原因: 複数のスレッドから同時に
QList::emplace_back()
を呼び出すと、データ競合が発生し、未定義動作やクラッシュの原因となる可能性があります。QList
はスレッドセーフではありません(読み取り専用の場合はスレッドセーフな場合がありますが、書き込みはスレッドセーフではありません)。 - トラブルシューティング:
- 複数のスレッドから
QList
に要素を追加する場合は、ミューテックス (QMutex
など) を使用してアクセスを同期させてください。 - Qt Concurrent や
Qt::BlockingQueuedConnection
など、Qt が提供するスレッド間のデータ受け渡しメカニズムを検討してください。
- 複数のスレッドから
- 原因: 複数のスレッドから同時に
- Qt Forum や Stack Overflow で検索する: 似たような問題に遭遇している人がいないか、オンラインのコミュニティで検索してみると解決策が見つかることがあります。
- Qt のドキュメントを参照する:
QList::emplace_back()
の公式ドキュメントには、詳細な説明や使用例、注意点などが記載されています。 - デバッガを使用する: 実行時エラーの場合、デバッガを使用してプログラムの実行をステップ実行し、変数の値やコールスタックを確認することで、問題の原因を特定できます。
- シンプルなコードで再現する: 複雑な状況でエラーが発生している場合、最小限のコードで同じエラーを再現してみることで、問題の切り分けが容易になります。
- エラーメッセージをよく読む: コンパイラのエラーメッセージは、問題の箇所と原因を示す重要なヒントです。特に「候補関数 (candidate function)」のリストが含まれている場合、なぜその関数が選択されなかったのか(引数の不一致など)が示されています。
QList::emplace_back()
の基本的な使用例
emplace_back()
は、QList
の末尾に要素を直接構築するために使用されます。引数は、格納される型 T
のコンストラクタに直接渡されます。
例1: プリミティブ型(整数)
#include <QCoreApplication>
#include <QList>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> intList;
// emplace_back を使用して int を直接構築
// int のコンストラクタは int (つまり引数そのもの) です
intList.emplace_back(10);
intList.emplace_back(20);
intList.emplace_back(30);
qDebug() << "intList:";
for (int value : intList) {
qDebug() << value;
}
// emplace_back は追加された要素への参照を返します
int& ref = intList.emplace_back(40);
ref += 5; // 追加された要素を直接変更
qDebug() << "Modified last element:" << intList.last(); // 45
return a.exec();
}
出力例
intList:
10
20
30
Modified last element: 45
解説
int
のようなプリミティブ型の場合、emplace_back
は渡された値を直接リストに格納します。戻り値の参照を使って、追加直後の要素をすぐに操作できる点がわかります。
カスタムクラスでの使用例(推奨される使い方)
オブジェクトのコピーやムーブのオーバーヘッドを削減したい場合に emplace_back()
が真価を発揮します。
例2: カスタムクラス Person
#include <QCoreApplication>
#include <QList>
#include <QString>
#include <QDebug>
class Person {
public:
// コンストラクタ (複数の引数を受け取る)
Person(const QString& name, int age) : m_name(name), m_age(age) {
qDebug() << "Person constructed: " << m_name << ", " << m_age;
}
// コピーコンストラクタ (呼び出されるとメッセージを表示)
Person(const Person& other) : m_name(other.m_name), m_age(other.m_age) {
qDebug() << "Person COPY constructed: " << m_name;
}
// ムーブコンストラクタ (呼び出されるとメッセージを表示)
Person(Person&& other) noexcept : m_name(std::move(other.m_name)), m_age(other.m_age) {
qDebug() << "Person MOVE constructed: " << m_name;
}
// アクセッサ
QString name() const { return m_name; }
int age() const { return m_age; }
private:
QString m_name;
int m_age;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<Person> peopleList;
qDebug() << "--- Using append() with temporary object ---";
// 一時オブジェクトを構築し、それをコピー(またはムーブ)して追加
peopleList.append(Person("Alice", 30));
qDebug() << "Current list size:" << peopleList.size(); // 1
qDebug() << "--- Using emplace_back() ---";
// emplace_back を使用して、QList 内で直接 Person オブジェクトを構築
peopleList.emplace_back("Bob", 25);
peopleList.emplace_back("Charlie", 35);
qDebug() << "Current list size:" << peopleList.size(); // 3
qDebug() << "--- Listing people ---";
for (const Person& p : peopleList) {
qDebug() << "Name:" << p.name() << ", Age:" << p.age();
}
// emplace_back の戻り値の参照を使って直接操作
qDebug() << "--- Modifying last element via reference ---";
Person& lastPerson = peopleList.emplace_back("David", 40);
// age を直接変更するセッターがないので、例として何か表示
qDebug() << "Newly added person via ref: " << lastPerson.name();
// lastPerson.m_age = 41; // プライベートメンバーなので直接アクセスはできない
// publicなセッターがあれば変更可能
return a.exec();
}
出力例 (環境やQtのバージョンによって若干異なる可能性あり)
--- Using append() with temporary object ---
Person constructed: "Alice" , 30 // 一時オブジェクト構築
Person MOVE constructed: "Alice" // QListにムーブされる
Current list size: 1
--- Using emplace_back() ---
Person constructed: "Bob" , 25 // QList内で直接構築
Person constructed: "Charlie" , 35 // QList内で直接構築
Current list size: 3
--- Listing people ---
Name: "Alice" , Age: 30
Name: "Bob" , Age: 25
Name: "Charlie" , Age: 35
--- Modifying last element via reference ---
Person constructed: "David" , 40 // QList内で直接構築
Newly added person via ref: "David"
解説
emplace_back("Bob", 25)
の場合、QList
の内部でPerson
オブジェクトが直接構築されています。そのため、Person constructed
のメッセージのみが表示され、中間的なコピー/ムーブのステップが省略されていることがわかります。これがemplace_back
の主な利点です。append(Person("Alice", 30))
の場合、まずPerson("Alice", 30)
という一時オブジェクトが構築され、その後、そのオブジェクトがQList
にムーブ(またはコピー)されています。そのため、コンストラクタとムーブコンストラクタの両方が呼び出されています。
QList<MyObject*>
のように生ポインタを格納するとメモリ管理が複雑になります。スマートポインタ (QSharedPointer
, std::shared_ptr
, std::unique_ptr
など) を使用することで、メモリリークのリスクを減らし、より安全なコードを書くことができます。
例3: QSharedPointer
を使用した emplace_back()
#include <QCoreApplication>
#include <QList>
#include <QSharedPointer> // QSharedPointer を使う
#include <QString>
#include <QDebug>
class Gadget {
public:
Gadget(const QString& model) : m_model(model) {
qDebug() << "Gadget constructed: " << m_model;
}
~Gadget() { // デストラクタで解放を確認
qDebug() << "Gadget destructed: " << m_model;
}
QString model() const { return m_model; }
private:
QString m_model;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<QSharedPointer<Gadget>> gadgetList;
qDebug() << "--- Adding Gadgets ---";
// QSharedPointer<Gadget> を直接構築
// Gadget のコンストラクタの引数を直接渡す
gadgetList.emplace_back(new Gadget("Phone X")); // QSharedPointer が new Gadget(...) を管理
gadgetList.emplace_back(new Gadget("Tablet Y"));
gadgetList.emplace_back(new Gadget("Watch Z"));
qDebug() << "--- Listing Gadgets ---";
for (const QSharedPointer<Gadget>& gadgetPtr : gadgetList) {
qDebug() << "Model:" << gadgetPtr->model();
}
qDebug() << "--- Clearing list ---";
// QList がクリアされると、QSharedPointer もスコープを抜けるため、
// 参照カウントが0になれば Gadget オブジェクトが自動的に解放される
gadgetList.clear();
qDebug() << "List cleared.";
return a.exec();
}
出力例
--- Adding Gadgets ---
Gadget constructed: "Phone X"
Gadget constructed: "Tablet Y"
Gadget constructed: "Watch Z"
--- Listing Gadgets ---
Model: "Phone X"
Model: "Tablet Y"
Model: "Watch Z"
--- Clearing list ---
Gadget destructed: "Watch Z"
Gadget destructed: "Tablet Y"
Gadget destructed: "Phone X"
List cleared.
QList
がclear()
されたり、main
関数が終了してgadgetList
がスコープを抜けると、QSharedPointer
オブジェクトも破棄され、その結果、Gadget
オブジェクトの参照カウントがゼロになればデストラクタが呼び出され、メモリが自動的に解放されることが出力から確認できます。emplace_back(new Gadget("Phone X"))
のように、new
で構築したポインタをemplace_back
に直接渡すことで、QSharedPointer
がそのポインタを管理するように直接構築されます。QList<QSharedPointer<Gadget>>
のように、QList
にスマートポインタを格納することで、Gadget
オブジェクトのメモリ管理をQSharedPointer
に任せることができます。
QList<T>::emplace_back()
の主な代替方法
大きく分けて以下の3つの方法があります。
QList<T>::append(const T &value)
/QList<T>::push_back(const T &value)
QList<T>::append(T &&value)
/QList<T>::push_back(T &&value)
(C++11以降のムーブセマンティクス)QList<T>::insert(int i, const T &value)
(指定位置への挿入)- ポインタを格納し、手動でオブジェクトを構築・管理する
それぞれについて詳しく見ていきましょう。
QList<T>::append(const T &value) または QList<T>::push_back(const T &value)
これは最も一般的で伝統的な方法です。既存のオブジェクトのコピーをリストの末尾に追加します。
特徴:
- 戻り値:
void
を返します。追加された要素への参照は直接得られません。 - コピーオーバーヘッド: オブジェクトがリストに追加される前に、元のオブジェクトからコピーコンストラクタが呼び出されます。オブジェクトが大きい、またはコピーコストが高い場合、パフォーマンスに影響を与える可能性があります。
- シンプルさ: 既存のオブジェクトをコピーするだけなので、理解しやすいです。
使用例:
#include <QCoreApplication>
#include <QList>
#include <QString>
#include <QDebug>
class Product {
public:
Product(const QString& name, double price) : m_name(name), m_price(price) {
qDebug() << "Product constructed: " << m_name;
}
Product(const Product& other) : m_name(other.m_name), m_price(other.m_price) {
qDebug() << "Product COPY constructed: " << m_name;
}
// ムーブコンストラクタも定義しておくと、append()がムーブを試みる
Product(Product&& other) noexcept : m_name(std::move(other.m_name)), m_price(other.m_price) {
qDebug() << "Product MOVE constructed: " << m_name;
}
QString name() const { return m_name; }
double price() const { return m_price; }
private:
QString m_name;
double m_price;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<Product> productList;
qDebug() << "--- Using append() with temporary object ---";
// 一時オブジェクトを構築し、それをリストにムーブまたはコピーする
productList.append(Product("Laptop", 1200.0));
// Product constructed: Laptop (一時オブジェクトの構築)
// Product MOVE constructed: Laptop (一時オブジェクトのムーブ)
qDebug() << "--- Using append() with lvalue object ---";
Product smartphone("Smartphone", 800.0); // オブジェクトを先に構築
productList.append(smartphone); // smartphone オブジェクトをコピー
// Product constructed: Smartphone
// Product COPY constructed: Smartphone
qDebug() << "--- Products in list ---";
for (const Product& p : productList) {
qDebug() << p.name() << ":" << p.price();
}
return a.exec();
}
適した状況:
- 既に存在するオブジェクトをリストに追加したい場合。
- C++11 以前の標準を使用している場合(
emplace_back
やムーブセマンティクスがないため)。 - オブジェクトが小さく、コピーコストが低い場合。
QList<T>::append(T &&value) または QList<T>::push_back(T &&value)
C++11 で導入されたムーブセマンティクスを利用した方法です。右辺値参照 (&&
) を受け取るオーバーロードで、オブジェクトの所有権を移動(ムーブ)することで、不必要なコピーを避けます。
- 戻り値:
void
を返します。 - オブジェクトの存在:
emplace_back
と異なり、一度どこかでオブジェクトが構築されてからムーブされます。 - ムーブセマンティクス:
emplace_back
と同様に、不必要なコピーコンストラクタの呼び出しを避けることができます。オブジェクトのデータが新しい場所へ「移動」するだけで、再構築の必要がないため、emplace_back
に近いパフォーマンスを発揮します。
上記の append()
の例で、一時オブジェクトを渡す場合がこれに該当します。
#include <QCoreApplication>
#include <QList>
#include <QString>
#include <QDebug>
// Product クラスは上記と同じ定義を使用
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<Product> productList;
qDebug() << "--- Using append() with rvalue (move semantics) ---";
// Product("Monitor", 300.0) は一時オブジェクト(右辺値)なので、ムーブコンストラクタが呼び出される
productList.append(Product("Monitor", 300.0));
// Product constructed: Monitor
// Product MOVE constructed: Monitor
qDebug() << "--- Products in list ---";
for (const Product& p : productList) {
qDebug() << p.name() << ":" << p.price();
}
return a.exec();
}
emplace_back
のように「直接構築」するのではなく、「既存のオブジェクトをムーブ」したい場合。- 一時オブジェクトや、それ以上使用しないオブジェクトをリストに追加したい場合。
- C++11 以降の標準を使用している場合。
QList<T>::insert(int i, const T &value)
リストの末尾だけでなく、任意の指定位置に要素を挿入したい場合に使用します。
- 戻り値:
void
を返します。 - パフォーマンス: 挿入位置より後ろにある要素はすべて移動(コピーまたはムーブ)されるため、リストの途中に挿入するとパフォーマンスコストが高くなる可能性があります。末尾への挿入であれば
append()
と同等です。 - 位置指定: 特定のインデックスに要素を挿入できます。
#include <QCoreApplication>
#include <QList>
#include <QString>
#include <QDebug>
// Product クラスは上記と同じ定義を使用
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<Product> productList;
productList.append(Product("Chair", 50.0));
productList.append(Product("Desk", 200.0));
qDebug() << "--- List before insertion ---";
for (const Product& p : productList) {
qDebug() << p.name();
}
qDebug() << "--- Inserting at index 1 ---";
productList.insert(1, Product("Lamp", 30.0)); // インデックス1に挿入
// Product constructed: Lamp
// Product MOVE constructed: Lamp
qDebug() << "--- List after insertion ---";
for (const Product& p : productList) {
qDebug() << p.name();
}
return a.exec();
}
出力例
--- List before insertion ---
Chair
Desk
--- Inserting at index 1 ---
Product constructed: "Lamp"
Product MOVE constructed: "Lamp"
--- List after insertion ---
Chair
Lamp
Desk
- 要素をリストの末尾以外に挿入する必要がある場合。
QList<T*>
のように生ポインタを格納し、オブジェクトの構築と破棄を開発者が手動で行う方法です。emplace_back
が導入される以前は、動的にオブジェクトを生成してリストに格納する際に用いられることがありました。
- パフォーマンス: オブジェクトそのものはコピーされませんが、ポインタがコピーされます。オブジェクトの動的確保にはオーバーヘッドが伴います。
- ポインタの安全性: ダングリングポインタやヌルポインタの問題が発生しやすくなります。
- メモリ管理の責任:
new
で確保したメモリは、delete
で明示的に解放する必要があります。これを怠るとメモリリークが発生します。 - 完全な制御: オブジェクトのライフサイクルを完全に制御できます。
#include <QCoreApplication>
#include <QList>
#include <QString>
#include <QDebug>
// Product クラスは上記と同じ定義を使用
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<Product*> productPtrList; // Product* のリスト
qDebug() << "--- Adding Product pointers ---";
// New 演算子でオブジェクトを構築し、そのポインタをリストに追加
productPtrList.append(new Product("Mouse", 25.0));
// Product constructed: Mouse
productPtrList.append(new Product("Keyboard", 75.0));
// Product constructed: Keyboard
qDebug() << "--- Products via pointers ---";
for (const Product* p : productPtrList) {
qDebug() << p->name() << ":" << p->price();
}
qDebug() << "--- Cleaning up memory ---";
// 非常に重要: リスト内のポインタが指すオブジェクトをすべて手動で削除
for (Product* p : qAsConst(productPtrList)) { // Qt6ではqAsConstが不要な場合あり
delete p;
}
productPtrList.clear(); // ポインタをリストから削除(オブジェクトは既に削除済み)
qDebug() << "Memory cleaned up.";
return a.exec();
}
--- Adding Product pointers ---
Product constructed: "Mouse"
Product constructed: "Keyboard"
--- Products via pointers ---
Mouse : 25
Keyboard : 75
--- Cleaning up memory ---
Product destructed: "Mouse"
Product destructed: "Keyboard"
Memory cleaned up.
- 非推奨: 現代のC++では、メモリリークやポインタ関連のエラーを防ぐために、スマートポインタ(
QSharedPointer
,std::unique_ptr
など)を使用することが強く推奨されます。スマートポインタを使用すれば、上記の例のようにQList<QSharedPointer<Product>>
のようにすることで、メモリ管理の責任を自動化できます。 - Qt のコンテナのライフサイクルとは独立して、オブジェクトのライフサイクルを厳密に制御したい特殊なケース。
- C++11 以前の環境で、
emplace_back
やスマートポインタが利用できない場合。
方法 | 特徴 | 利点 | 欠点 | 適した状況 |
---|---|---|---|---|
append(const T&) | 既存オブジェクトのコピーを末尾に追加。 | シンプルで理解しやすい。 | コピーオーバーヘッド。 | 小さいオブジェクト、C++11以前、既存オブジェクトの追加。 |
append(T&&) | 一時オブジェクトやムーブ可能なオブジェクトのムーブを末尾に追加。 | コピーオーバーヘッドなし。emplace_back に近いパフォーマンス。 | オブジェクトは一度構築済み。 | C++11以降、一時オブジェクトの追加、大規模なオブジェクトのムーブ。 |
insert(int, const T&/T&&) | 指定位置にオブジェクトをコピーまたはムーブで挿入。 | 柔軟な挿入位置。 | 挿入位置によっては要素の移動によるパフォーマンスコストが高い。 | 挿入位置を制御したい場合。 |
emplace_back() (比較対象) | 引数からリスト内でオブジェクトを直接構築。 | コピー・ムーブオーバーヘッドなし。最高のパフォーマンス。追加後に参照取得可。 | C++11以降。 | 大規模なオブジェクトの効率的な追加、追加直後のオブジェクト操作。 |
ポインタ格納 (QList<T*> ) | オブジェクトをヒープに確保し、そのポインタをリストに格納。手動でメモリ管理。 | オブジェクトのライフサイクルを完全に制御。 | メモリリークのリスク大。 ポインタの安全性問題。手動での解放が必要。 | 非推奨。スマートポインタが使用できないごく限られたレガシーコードの場合のみ。 |