// ============================================================================
//  qt_pitfall_lambda_capture —— 坑点最小复现：lambda 槽的生命周期与第五参数
//  对应教案：OUTLINE_4DAY.md Day2 下午 ★坑卡「lambda 槽的生命周期与第五参数」
//  配套文档：docs/teaching/examples/pitfall_lambda_capture/README.md
//
//  默认运行：对比两条连接——不带 context 的 lambda 在接收者销毁后**仍会被调用**，
//  带 context（connect 第五参数）的连接随接收者销毁自动断开。退出码 0。
//  加 --dangle：不带 context 的 lambda 里真的去访问已销毁的接收者（悬空指针，
//  UB，课堂演示专用——可能打印垃圾值、也可能直接崩，两种结果都是教学素材）。
// ============================================================================

#include <QCoreApplication>
#include <QDebug>
#include <QObject>
#include <cstring>

class Sender : public QObject {
    Q_OBJECT
public:
    using QObject::QObject;
signals:
    void ping();
};

class Receiver : public QObject {
    Q_OBJECT
public:
    explicit Receiver(QObject* parent = nullptr)
        : QObject(parent), m_name(QStringLiteral("接收者甲")) {}
    QString name() const { return m_name; }
private:
    QString m_name;
};

int main(int argc, char* argv[]) {
    QCoreApplication app(argc, argv);
    const bool dangle = (argc > 1 && std::strcmp(argv[1], "--dangle") == 0);

    Sender sender;
    auto* receiver = new Receiver;

    // 连接 A（错误示范）：lambda 捕获了 receiver 指针，但没有传第五参数。
    // 这条连接的生存期只跟 sender 挂钩——receiver 死了它也不会断开。
    QObject::connect(&sender, &Sender::ping, [receiver, dangle]() {
        qInfo() << "[无 context] lambda 被调用";
        if (dangle) {
            // 悬空访问：第二次触发时 receiver 已被 delete —— UB
            qInfo() << "         访问 receiver->name() =" << receiver->name();
        }
    });

    // 连接 B（正确写法）：第五参数传 receiver 作为 context object，
    // receiver 析构时 Qt 自动断开这条连接，lambda 不可能再被调用。
    QObject::connect(&sender, &Sender::ping, receiver, [receiver]() {
        qInfo() << "[带 context] lambda 被调用，receiver->name() =" << receiver->name();
    });

    qInfo() << "---- 第一次 emit（receiver 活着，两条连接都触发）----";
    emit sender.ping();

    delete receiver;
    qInfo() << "---- delete receiver 之后，第二次 emit ----";
    emit sender.ping();   // 只有连接 A 还会触发——这就是悬空风险所在

    qInfo() << "结论：lambda 槽捕获了对象指针，就必须把该对象作为 connect 的"
               "第五参数（context），让连接的生命周期跟着对象走。";
    return 0;
}

#include "qt_pitfall_lambda_capture.moc"
