Qt/C++入門:2024年版 クロスプラットフォームGUIアプリケーション開発完全ガイド

Qt/C++開発の基礎知識

QtフレームワークとC++の相性の良さ

QtフレームワークとC++は、高性能なGUIアプリケーション開発において理想的な組み合わせです。その主な理由は以下の通りです:

  1. 型安全性と実行時性能
  • C++の強力な型システムとQtのメタオブジェクトシステムの統合
  • コンパイル時のエラーチェックによる堅牢性の向上
  • 直接的なハードウェアアクセスによる高いパフォーマンス
// Qtのシグナル/スロットシステムの例
class Calculator : public QObject {
    Q_OBJECT  // メタオブジェクトマクロ

public:
    Calculator() {}

public slots:
    // 型安全なスロット定義
    void calculate(int value) {
        emit resultReady(value * 2);
    }

signals:
    // 型安全なシグナル定義
    void resultReady(int result);
};
  1. メモリ管理の柔軟性
  • QObjectベースの親子関係による自動メモリ管理
  • スマートポインタとの親和性が高い

開発効率を劇的に向上させるQtの特徴

  1. クロスプラットフォーム開発の効率化
  • 単一のコードベースで複数プラットフォームに対応
  • プラットフォーム固有の最適化が自動的に適用
// プラットフォーム固有の処理の例
#ifdef Q_OS_WIN
    QString configPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
#elif defined(Q_OS_MAC)
    QString configPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
#else
    QString configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
#endif
  1. 豊富なビルトインウィジェット
  • 一貫性のあるルック&フィール
  • カスタマイズ可能なテーマシステム
  • アクセシビリティのサポート
  1. 統合開発環境(Qt Creator)のサポート
  • ビジュアルデザイナー
  • デバッグツール
  • プロファイリング機能

2024年におけるQt/C++の市場動向

  1. 企業での採用傾向
  • 自動車産業:インフォテインメントシステム
  • 医療機器:診断・モニタリング装置
  • 産業用機器:制御システム
  1. 最新のトレンド
  • Qt Quick/QMLの普及
    • デクララティブUIデザイン
    • モバイルアプリケーション開発の効率化
// モダンなQMLによるUI定義の例
Rectangle {
    width: 200
    height: 100
    color: "lightblue"

    Text {
        anchors.centerIn: parent
        text: "Hello Qt Quick!"
        font.pixelSize: 20
    }

    MouseArea {
        anchors.fill: parent
        onClicked: console.log("Clicked!")
    }
}
  1. 将来の展望
  • WebAssemblyサポートの強化
  • AIと機械学習の統合
  • モバイル/組込み開発の更なる簡素化

Qt/C++の組み合わせは、2024年においても高性能なクロスプラットフォームアプリケーション開発の強力な選択肢であり続けています。特に、組込みシステムやデスクトップアプリケーションの分野では、その価値が高く評価されています。

Qt/C++開発環境の構築方法

最新のQt Creatorのインストールと初期設定

  1. インストール準備
  • Qt公式サイトから最新のQt Online Installerをダウンロード
  • 必要なディスク容量: 最低10GB以上を推奨
  • 必要なシステム要件の確認
  1. インストール手順
# Linuxの場合のインストール例
chmod +x qt-unified-linux-x64-4.6.1-online.run
./qt-unified-linux-x64-4.6.1-online.run
  1. 必要なコンポーネントの選択
  • Qt 6.6.1(最新LTS版)
  • Qt Creator 12.0
  • 必要なプラットフォーム用のコンパイラ
  • デバッグツール
  1. 初期設定の最適化
// Qt Creatorの推奨設定
// Tools > Options で以下を設定

// ビルド設定
CMAKE_PREFIX_PATH=/path/to/qt/6.6.1/gcc_64/lib/cmake
CMAKE_BUILD_TYPE=Debug  // 開発時

// コード補完
ClangFormat: Enable
ClangTidy: Enable

効率的な開発のためのプラグイン検討

  1. 必須プラグイン
  • ClangFormat: コードフォーマット
  • Qt Design Studio Integration: UIデザイン
  • Git Integration: バージョン管理
  1. おすすめプラグイン
  • Qt Quick Preview: QMLのライブプレビュー
  • Valgrind: メモリリーク検出
  • Qt Installer Framework: デプロイメント
// プラグインの設定例(ClangFormat)
// .clang-format ファイルの例
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 100
AllowShortFunctionsOnASingleLine: Empty
BreakBeforeBraces: Allman

クロスプラットフォーム開発環境の最適化

  1. マルチプラットフォーム対応の設定
# CMakeLists.txtの例
cmake_minimum_required(VERSION 3.16)
project(cross_platform_app VERSION 1.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)

if(WIN32)
    set(PLATFORM_SPECIFIC_SOURCES windows_specific.cpp)
elseif(APPLE)
    set(PLATFORM_SPECIFIC_SOURCES macos_specific.mm)
else()
    set(PLATFORM_SPECIFIC_SOURCES linux_specific.cpp)
endif()

add_executable(${PROJECT_NAME}
    main.cpp
    mainwindow.cpp
    ${PLATFORM_SPECIFIC_SOURCES}
)

target_link_libraries(${PROJECT_NAME} PRIVATE
    Qt6::Core
    Qt6::Gui
    Qt6::Widgets
)
  1. プラットフォーム固有の最適化
  • Windows: MSVCコンパイラの最適化設定
  • macOS: Code Signing設定
  • Linux: パッケージング設定
  1. デバッグ環境の整備
  • GDB/LLDBの設定
  • プラットフォーム別デバッグシンボルの生成
  • リモートデバッグの設定
  1. パフォーマンス測定ツールの導入
# プラットフォーム別プロファイリングツール
Windows: Visual Studio Profiler
macOS: Instruments
Linux: perf, valgrind
  1. 継続的インテグレーション(CI)の設定
# .github/workflows/build.yml の例
name: Build
on: [push, pull_request]

jobs:
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]

    steps:
    - uses: actions/checkout@v2
    - name: Install Qt
      uses: jurplel/install-qt-action@v3
      with:
        version: '6.6.1'
    - name: Build
      run: |
        cmake -B build -S .
        cmake --build build

これらの設定を適切に行うことで、効率的なクロスプラットフォーム開発環境を構築することができます。特に、自動化とツールの活用により、開発効率を大きく向上させることが可能です。

実践的なQt/C++プログラミング手法

モダンC++とQtのテクニックの組み合わせ

  1. スマートポインタの活用
// 従来の方法
QLabel* label = new QLabel("Hello");
// メモリリークの可能性あり

// モダンな方法
auto label = std::make_unique<QLabel>("Hello");
// または
auto label = std::make_shared<QLabel>("Hello");

// Qtのペアレント管理との組み合わせ
class MainWindow : public QMainWindow
{
    std::unique_ptr<QLabel> m_label;
public:
    MainWindow()
    {
        m_label = std::make_unique<QLabel>("Hello", this);
        // thisをペアレントとして設定
    }
};
  1. ラムダ式とシグナル/スロットの組み合わせ
// モダンな接続方法
connect(button, &QPushButton::clicked, [this](bool checked) {
    // キャプチャリストで this を明示的にキャプチャ
    m_label->setText(checked ? "ON" : "OFF");
});

// 型安全な接続
connect(button, QOverload<bool>::of(&QPushButton::clicked), 
    this, &MainWindow::handleClick);
  1. C++17/20の機能活用
// 構造化バインディング
auto [x, y] = widget->pos();

// std::optionalの活用
std::optional<QColor> getHighlightColor(const QString& theme)
{
    if (theme == "dark")
        return QColor(255, 128, 0);
    return std::nullopt;
}

// rangeベースforループとQtコンテナ
for (const auto& item : qAsConst(items)) {
    // constな参照でアクセス
}

メモリ管理の最適化とベストプラクティス

  1. QObjectの所有権管理
class ResourceManager
{
private:
    QObject* m_parent;
    QMap<QString, QObject*> m_resources;

public:
    explicit ResourceManager(QObject* parent)
        : m_parent(parent)
    {
    }

    template<typename T>
    T* createResource(const QString& name)
    {
        // 親子関係による自動削除
        auto resource = new T(m_parent);
        m_resources[name] = resource;
        return resource;
    }
};
  1. メモリリーク防止パターン
// RAll原則の活用
class ScopedTransaction
{
    QSqlDatabase& db;
public:
    explicit ScopedTransaction(QSqlDatabase& db) : db(db)
    {
        db.transaction();
    }
    ~ScopedTransaction()
    {
        if (db.isOpen())
            db.rollback();
    }
    void commit()
    {
        db.commit();
    }
};
  1. キャッシュ戦略
class ImageCache
{
private:
    QCache<QString, QPixmap> m_cache;

public:
    ImageCache() : m_cache(1024 * 1024) // 1MB制限
    {
    }

    QPixmap getImage(const QString& path)
    {
        if (auto* cached = m_cache.object(path))
            return *cached;

        QPixmap pixmap(path);
        m_cache.insert(path, new QPixmap(pixmap));
        return pixmap;
    }
};

効果的なシグナル/スロットの活用方法

  1. カスタムシグナルの定義
class DataProcessor : public QObject
{
    Q_OBJECT

signals:
    void progressUpdated(int percentage, const QString& status);
    void processingComplete(const QVariantMap& results);
    void errorOccurred(const QString& message);

public slots:
    void processData(const QByteArray& data)
    {
        try {
            emit progressUpdated(0, "開始中...");
            // データ処理
            emit progressUpdated(50, "処理中...");
            // 処理完了
            QVariantMap results;
            emit processingComplete(results);
        }
        catch (const std::exception& e) {
            emit errorOccurred(QString::fromStdString(e.what()));
        }
    }
};
  1. 非同期処理の実装
class Worker : public QObject
{
    Q_OBJECT

public slots:
    void doWork()
    {
        // 重い処理
        QThread::msleep(1000);
        emit finished();
    }

signals:
    void finished();
};

// メインウィンドウでの使用
void MainWindow::startAsyncWork()
{
    auto* worker = new Worker;
    auto* thread = new QThread;

    worker->moveToThread(thread);

    connect(thread, &QThread::started, worker, &Worker::doWork);
    connect(worker, &Worker::finished, thread, &QThread::quit);
    connect(worker, &Worker::finished, worker, &Worker::deleteLater);
    connect(thread, &QThread::finished, thread, &QThread::deleteLater);

    thread->start();
}
  1. イベントフィルタの活用
class CustomWidget : public QWidget
{
protected:
    bool eventFilter(QObject* watched, QEvent* event) override
    {
        if (watched == childWidget && event->type() == QEvent::MouseButtonPress) {
            auto* mouseEvent = static_cast<QMouseEvent*>(event);
            handleCustomMousePress(mouseEvent);
            return true; // イベントを処理済みとしてマーク
        }
        return QWidget::eventFilter(watched, event);
    }
};

これらのテクニックを適切に組み合わせることで、メンテナンス性が高く、パフォーマンスの良いQtアプリケーションを開発することができます。特に、モダンC++の機能とQtのシグナル/スロットシステムを組み合わせることで、より安全で効率的なコードを書くことが可能です。

GUIアプリケーション開発の実践ステップ

ステップ 1:基本的なウィジェットの実装

  1. メインウィンドウの作成
// mainwindow.h
class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    QWidget* centralWidget;
    QVBoxLayout* mainLayout;
    QPushButton* submitButton;
    QLineEdit* inputField;
    QLabel* statusLabel;

public:
    MainWindow(QWidget* parent = nullptr)
        : QMainWindow(parent)
    {
        setupUi();
        connectSignals();
    }

private:
    void setupUi();
    void connectSignals();
};

// mainwindow.cpp
void MainWindow::setupUi()
{
    // 中央ウィジェットの設定
    centralWidget = new QWidget(this);
    setCentralWidget(centralWidget);

    // レイアウトの設定
    mainLayout = new QVBoxLayout(centralWidget);

    // 各ウィジェットの作成と追加
    inputField = new QLineEdit(this);
    submitButton = new QPushButton("送信", this);
    statusLabel = new QLabel("準備完了", this);

    mainLayout->addWidget(inputField);
    mainLayout->addWidget(submitButton);
    mainLayout->addWidget(statusLabel);
}
  1. カスタムウィジェットの作成
// customwidget.h
class CustomWidget : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)

private:
    QString m_value;

public:
    explicit CustomWidget(QWidget* parent = nullptr);
    QString value() const { return m_value; }

public slots:
    void setValue(const QString& value);

signals:
    void valueChanged(const QString& newValue);
};

ステップ 2:レイアウト設計とスタイルの適用

  1. 複雑なレイアウトの構築
void MainWindow::createComplexLayout()
{
    auto* gridLayout = new QGridLayout;

    // ツールバー領域
    auto* toolbar = new QToolBar;
    gridLayout->addWidget(toolbar, 0, 0, 1, 2);

    // サイドパネル
    auto* sidePanel = new QWidget;
    auto* sidePanelLayout = new QVBoxLayout(sidePanel);
    gridLayout->addWidget(sidePanel, 1, 0);

    // メインコンテンツ領域
    auto* contentArea = new QWidget;
    auto* contentLayout = new QVBoxLayout(contentArea);
    gridLayout->addWidget(contentArea, 1, 1);

    // ステータスバー
    auto* statusBar = new QStatusBar;
    gridLayout->addWidget(statusBar, 2, 0, 1, 2);

    // グリッドの列幅の設定
    gridLayout->setColumnStretch(0, 1);  // サイドパネル
    gridLayout->setColumnStretch(1, 3);  // メインコンテンツ
}
  1. スタイルシートの適用
void MainWindow::applyStyles()
{
    // ダークテーマの適用
    QString styleSheet = R"(
        QMainWindow {
            background-color: #2b2b2b;
        }

        QPushButton {
            background-color: #0d47a1;
            color: white;
            border-radius: 4px;
            padding: 6px 12px;
            font-weight: bold;
        }

        QPushButton:hover {
            background-color: #1565c0;
        }

        QLineEdit {
            padding: 4px;
            border: 1px solid #666;
            border-radius: 4px;
            background-color: #333;
            color: white;
        }
    )";

    setStyleSheet(styleSheet);
}

ステップ 3:イベント処理の実装

  1. イベントハンドラの設定
void MainWindow::connectSignals()
{
    // クリックイベントの処理
    connect(submitButton, &QPushButton::clicked, this, [this]() {
        QString input = inputField->text();
        if (!input.isEmpty()) {
            processInput(input);
        } else {
            showError("入力が必要です");
        }
    });

    // テキスト変更イベントの処理
    connect(inputField, &QLineEdit::textChanged, this, [this](const QString& text) {
        submitButton->setEnabled(!text.isEmpty());
    });
}
  1. カスタムイベントの処理
class CustomEvent : public QEvent
{
public:
    static const QEvent::Type CustomEventType = static_cast<QEvent::Type>(QEvent::User + 1);

    CustomEvent(const QString& data)
        : QEvent(CustomEventType), m_data(data) {}

    QString data() const { return m_data; }

private:
    QString m_data;
};

// イベントの処理
bool MainWindow::event(QEvent* event)
{
    if (event->type() == CustomEvent::CustomEventType) {
        auto* customEvent = static_cast<CustomEvent*>(event);
        handleCustomEvent(customEvent->data());
        return true;
    }
    return QMainWindow::event(event);
}
  1. ドラッグ&ドロップの実装
class DropArea : public QLabel
{
protected:
    void dragEnterEvent(QDragEnterEvent* event) override
    {
        if (event->mimeData()->hasFormat("text/plain"))
            event->acceptProposedAction();
    }

    void dropEvent(QDropEvent* event) override
    {
        const QMimeData* mimeData = event->mimeData();
        if (mimeData->hasText()) {
            setText(mimeData->text());
            emit textDropped(mimeData->text());
        }
    }

signals:
    void textDropped(const QString& text);
};

これらのステップを順番に実装することで、基本的なGUIアプリケーションの骨格を作ることができます。各ステップで重要なのは:

  • ウィジェットの適切な階層構造の設計
  • レスポンシブなレイアウトの実現
  • ユーザーフレンドリーなイベント処理の実装
  • パフォーマンスを考慮したイベントの最適化

特に、イベント処理では非同期処理を適切に組み込み、UIのフリーズを防ぐことが重要です。

パフォーマンス最適化とデバッグ技法

ボトルネックの特定と解決方法

  1. プロファイリングツールの活用
// Qt Creatorのプロファイラを使用した例
#include <QElapsedTimer>

class PerformanceMonitor
{
public:
    static void measureFunction(const QString& functionName, std::function<void()> func)
    {
        QElapsedTimer timer;
        timer.start();

        func();

        qDebug() << functionName << "実行時間:" << timer.elapsed() << "ms";
    }
};

// 使用例
void heavyOperation()
{
    PerformanceMonitor::measureFunction("heavyOperation", []() {
        // 重い処理
        for(int i = 0; i < 1000000; i++) {
            // 処理
        }
    });
}
  1. メモリ使用量の最適化
// メモリリークの検出と防止
class ResourceManager
{
private:
    QHash<QString, QSharedPointer<QResource>> m_resources;

public:
    void loadResource(const QString& path)
    {
        // リソースのプリロードと共有
        if (!m_resources.contains(path)) {
            m_resources[path] = QSharedPointer<QResource>::create(path);
        }
    }

    void clearUnusedResources()
    {
        // 参照カウントが1(このマネージャのみ)のリソースを解放
        auto it = m_resources.begin();
        while (it != m_resources.end()) {
            if (it.value().use_count() == 1) {
                it = m_resources.erase(it);
            } else {
                ++it;
            }
        }
    }
};

クロスプラットフォームにおける最適化戦略

  1. プラットフォーム固有の最適化
#ifdef Q_OS_WIN
    // Windowsでの描画最適化
    class WindowsOptimizer
    {
    public:
        static void optimizeForWindows(QWidget* widget)
        {
            widget->setAttribute(Qt::WA_NativeWindow);
            widget->setAttribute(Qt::WA_PaintOnScreen);
        }
    };
#elif defined(Q_OS_MAC)
    // macOSでの最適化
    class MacOptimizer
    {
    public:
        static void optimizeForMac(QWidget* widget)
        {
            widget->setAttribute(Qt::WA_MacShowFocusRect, false);
            widget->setAttribute(Qt::WA_MacMiniSize);
        }
    };
#endif
  1. レンダリングパフォーマンスの最適化
class OptimizedWidget : public QWidget
{
protected:
    void paintEvent(QPaintEvent* event) override
    {
        // ダブルバッファリングの活用
        QPixmap buffer(size());
        buffer.fill(Qt::transparent);

        QPainter bufferPainter(&buffer);
        // アンチエイリアシングの設定
        bufferPainter.setRenderHint(QPainter::Antialiasing);

        // クリッピング領域の設定
        bufferPainter.setClipRect(event->rect());

        // 実際の描画処理
        render(&bufferPainter);

        // スクリーンへの転送
        QPainter painter(this);
        painter.drawPixmap(0, 0, buffer);
    }
};

効率的なデバッグツールの活用テクニック

  1. デバッグログの実装
class Logger
{
public:
    enum class Level {
        Debug,
        Info,
        Warning,
        Error
    };

    static void log(Level level, const QString& message)
    {
        QString timestamp = QDateTime::currentDateTime()
            .toString("yyyy-MM-dd hh:mm:ss.zzz");

        QString levelStr;
        switch (level) {
            case Level::Debug:   levelStr = "DEBUG"; break;
            case Level::Info:    levelStr = "INFO"; break;
            case Level::Warning: levelStr = "WARN"; break;
            case Level::Error:   levelStr = "ERROR"; break;
        }

        QString logMessage = QString("[%1] [%2] %3")
            .arg(timestamp, levelStr, message);

        #ifdef QT_DEBUG
            qDebug().noquote() << logMessage;
        #endif

        // ファイルへの出力
        static QFile logFile("app.log");
        static QTextStream logStream(&logFile);

        if (!logFile.isOpen()) {
            logFile.open(QIODevice::WriteOnly | QIODevice::Append);
        }

        logStream << logMessage << Qt::endl;
    }
};
  1. デバッグビルドの最適化
// デバッグマクロの活用
#ifdef QT_DEBUG
    #define DEBUG_LOG(msg) Logger::log(Logger::Level::Debug, msg)
    #define ASSERT_GUI_THREAD Q_ASSERT(QThread::currentThread() == qApp->thread())
#else
    #define DEBUG_LOG(msg)
    #define ASSERT_GUI_THREAD
#endif

// メモリトラッキング
class MemoryTracker
{
public:
    static void trackAllocation(void* ptr, size_t size)
    {
        #ifdef QT_DEBUG
            qDebug() << "Memory allocated:" << ptr << "Size:" << size;
        #endif
    }

    static void trackDeallocation(void* ptr)
    {
        #ifdef QT_DEBUG
            qDebug() << "Memory deallocated:" << ptr;
        #endif
    }
};
  1. 条件付きブレークポイントの活用
void processData(const QVariant& data)
{
    // デバッグビルドでの型チェック
    #ifdef QT_DEBUG
        if (!data.canConvert<QString>()) {
            qDebug() << "Invalid data type:" << data.typeName();
            Q_ASSERT(false);  // 開発時のみブレーク
        }
    #endif

    // 通常の処理
    QString strData = data.toString();
    // ...
}

これらの最適化とデバッグ技法を適切に組み合わせることで、高品質なQtアプリケーションを開発することができます。特に:

  • パフォーマンスのボトルネックを早期に発見し対処する
  • プラットフォーム固有の最適化を適切に実装する
  • 効果的なデバッグ環境を構築する

これらの要素は、実際の開発現場で非常に重要な役割を果たします。

実践的なプロジェクト例と応用

シンプルなテキストエディタの作成手順

  1. 基本構造の実装
// texteditor.h
class TextEditor : public QMainWindow
{
    Q_OBJECT

private:
    QPlainTextEdit* m_editor;
    QString m_currentFile;

    QMenu* m_fileMenu;
    QMenu* m_editMenu;
    QToolBar* m_toolbar;

    // 検索機能用
    QLineEdit* m_searchBox;
    QPushButton* m_findButton;

public:
    TextEditor(QWidget* parent = nullptr);

private slots:
    void newFile();
    void openFile();
    void saveFile();
    void findText();
};

// texteditor.cpp
TextEditor::TextEditor(QWidget* parent)
    : QMainWindow(parent)
{
    setupEditor();
    setupMenus();
    setupToolBar();
    setupSearchWidget();

    setCentralWidget(m_editor);
    setWindowTitle(tr("Qt Text Editor"));
}

void TextEditor::setupEditor()
{
    m_editor = new QPlainTextEdit;
    m_editor->setLineWrapMode(QPlainTextEdit::NoWrap);

    // シンタックスハイライトの設定
    QFont font("Courier", 10);
    m_editor->setFont(font);

    // 行番号表示の実装
    auto lineNumberArea = new LineNumberArea(m_editor);
}
  1. 高度な機能の追加
// 自動保存機能
class AutoSaveManager : public QObject
{
    Q_OBJECT

private:
    QTimer m_timer;
    TextEditor* m_editor;

public:
    AutoSaveManager(TextEditor* editor, QObject* parent = nullptr)
        : QObject(parent), m_editor(editor)
    {
        connect(&m_timer, &QTimer::timeout,
                this, &AutoSaveManager::performAutoSave);

        m_timer.start(300000); // 5分ごとに自動保存
    }

private slots:
    void performAutoSave()
    {
        if (m_editor->isModified()) {
            m_editor->saveToTemp();
        }
    }
};

データベース連携アプリケーションの実装

  1. データベース接続の設定
// dbmanager.h
class DBManager
{
public:
    static DBManager& instance()
    {
        static DBManager instance;
        return instance;
    }

    bool initialize()
    {
        m_db = QSqlDatabase::addDatabase("QSQLITE");
        m_db.setDatabaseName("application.db");

        if (!m_db.open()) {
            qDebug() << "データベース接続エラー:" << m_db.lastError().text();
            return false;
        }

        createTables();
        return true;
    }

private:
    QSqlDatabase m_db;

    void createTables()
    {
        QSqlQuery query;
        query.exec(R"(
            CREATE TABLE IF NOT EXISTS documents (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                title TEXT NOT NULL,
                content TEXT,
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
                updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
            )
        )");
    }
};
  1. データアクセス層の実装
// documentrepository.h
class DocumentRepository
{
public:
    struct Document {
        int id;
        QString title;
        QString content;
        QDateTime createdAt;
        QDateTime updatedAt;
    };

    QVector<Document> getAllDocuments()
    {
        QVector<Document> documents;
        QSqlQuery query("SELECT * FROM documents ORDER BY updated_at DESC");

        while (query.next()) {
            Document doc;
            doc.id = query.value("id").toInt();
            doc.title = query.value("title").toString();
            doc.content = query.value("content").toString();
            doc.createdAt = query.value("created_at").toDateTime();
            doc.updatedAt = query.value("updated_at").toDateTime();
            documents.append(doc);
        }

        return documents;
    }

    bool saveDocument(Document& doc)
    {
        QSqlQuery query;
        if (doc.id == 0) {
            // 新規作成
            query.prepare(R"(
                INSERT INTO documents (title, content)
                VALUES (:title, :content)
            )");
        } else {
            // 更新
            query.prepare(R"(
                UPDATE documents
                SET title = :title,
                    content = :content,
                    updated_at = CURRENT_TIMESTAMP
                WHERE id = :id
            )");
            query.bindValue(":id", doc.id);
        }

        query.bindValue(":title", doc.title);
        query.bindValue(":content", doc.content);

        return query.exec();
    }
};

マルチスレッドアプリケーションの開発方法

  1. ワーカースレッドの実装
// worker.h
class Worker : public QObject
{
    Q_OBJECT

public slots:
    void processData(const QByteArray& data)
    {
        // 重い処理をバックグラウンドで実行
        QThread::msleep(1000); // シミュレーション

        // 結果を生成
        QByteArray result = processDataInBackground(data);

        // 処理完了を通知
        emit resultReady(result);
    }

signals:
    void resultReady(const QByteArray& result);

private:
    QByteArray processDataInBackground(const QByteArray& data)
    {
        // 実際の処理をここに実装
        return data;
    }
};

// mainwindow.cpp
void MainWindow::setupWorker()
{
    Worker* worker = new Worker;
    QThread* workerThread = new QThread;

    worker->moveToThread(workerThread);

    connect(workerThread, &QThread::finished,
            worker, &QObject::deleteLater);
    connect(this, &MainWindow::operate,
            worker, &Worker::processData);
    connect(worker, &Worker::resultReady,
            this, &MainWindow::handleResults);

    workerThread->start();
}
  1. スレッドプール実装
// threadpool.h
class ThreadPool : public QObject
{
    Q_OBJECT

private:
    QVector<QThread*> m_threads;
    QQueue<std::function<void()>> m_tasks;
    QMutex m_mutex;
    QWaitCondition m_condition;

public:
    ThreadPool(int threadCount = QThread::idealThreadCount())
    {
        for (int i = 0; i < threadCount; ++i) {
            auto thread = new QThread(this);
            connect(thread, &QThread::started, [this]() {
                threadFunction();
            });
            m_threads.append(thread);
            thread->start();
        }
    }

    void addTask(std::function<void()> task)
    {
        QMutexLocker locker(&m_mutex);
        m_tasks.enqueue(task);
        m_condition.wakeOne();
    }

private:
    void threadFunction()
    {
        while (true) {
            std::function<void()> task;

            {
                QMutexLocker locker(&m_mutex);
                while (m_tasks.isEmpty()) {
                    m_condition.wait(&m_mutex);
                }

                task = m_tasks.dequeue();
            }

            task();
        }
    }
};

これらの実装例は、実際の開発現場で必要となる主要な機能を含んでいます。特に重要なポイントは:

  • モジュール化された設計
  • エラーハンドリングの適切な実装
  • スレッドセーフな処理の実現
  • パフォーマンスを考慮した実装

これらの例を基に、要件に応じてカスタマイズすることで、実用的なアプリケーションを効率的に開発することができます。