Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← C++ 编程 / C++ Programming

Qt 与可视化 / Qt & Visualization

1. Qt 6 与科学可视化路线 / Qt 6 and Scientific Visualization Roadmap

2. 信号槽机制深度解析 / Qt Signals and Slots in Depth

3. 对象树与内存管理 / QObject Trees and Memory Management

4. CAE开发常用核心API实战 / Essential Qt APIs for CAE Development

5. Model/View架构与CAE数据处理 / Model-View Architecture and CAE Data Processing

6. 多线程与求解器集成 / Multithreading and Solver Integration

7. CAE软件架构实战模式 / Practical Architecture Patterns for CAE Software

8. 3D可视化与交互 / Interactive 3D Visualization

9. 实战案例:从零拆解一个FEA前后处理器 / Building an FEA Pre- and Post-Processor from Scratch

本页目录

CAE开发常用核心API实战 / Essential Qt APIs for CAE Development ​

📅 创建时间:2026-07-13 🏷️ 标签:#Qt #QString #QVariant #QSettings #QProcess #QTimer #CAE 📚 前置知识:Qt Cpp Overview、Object Tree Memory


📋 本章目标 ​

  • 理解Qt对C++标准库的扩展与增强——什么时候用Qt而非std
  • 掌握QString/QByteArray在CAE科学数据处理中的高效用法
  • 掌握QVariant作为CAE属性系统的核心容器
  • 掌握QSettings的应用/插件配置持久化策略
  • 掌握QFile/QDataStream/QTextStream的CAE文件I/O模式
  • 掌握QProcess启动外部求解器并双向通信
  • 掌握QTimer在仿真步进和动画中的应用
  • 能够在CAE项目中正确选择和使用这些API

专题扩展 ​

  • Qt 数据、I/O 与序列化兼容

第1部分:字符串处理——QString与QByteArray ​

1.1 QString vs std::string 对比 ​

┌─────────────────────────────────────────────────────────────┐
│             QString vs std::string 选型指南                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  场景                        推荐           原因             │
│  ────────────────────────  ──────────────  ──────────────   │
│  UI显示文本                  QString        内部UTF-16,边界显式转码│
│  文件路径                    QString        QFile需要的格式   │
│  科学计算数值字符串          QString        格式化/解析强大   │
│  求解器通讯(字节级)        QByteArray     原始二进制       │
│  与第三方C++库交互           std::string    标准接口          │
│  大量字符串拼接              QStringBuilder 零临时对象        │
│  JSON/XML处理                QString        QJsonDocument    │
│                                                             │
│  互转:                                                     │
│  QString → std::string:  str.toStdString()                  │
│  std::string → QString:  QString::fromStdString(str)        │
│  QString → QByteArray:   str.toUtf8()                       │
│  QByteArray → QString:   QString::fromUtf8(bytes)           │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

1.2 CAE科学数据格式化 ​

cpp
// ===== 数值 → 字符串(用于UI显示和文件输出)=====

// 基础格式化
double stress = 3.456789e+08;
QString s1 = QString::number(stress, 'g', 6);  // "3.45679e+08" (科学计数)
QString s2 = QString::number(stress, 'f', 2);  // "345678900.00" (定点)
QString s3 = QString("%1 MPa").arg(stress, 0, 'e', 4);  // "3.4568e+08 MPa"

// ===== 批量格式化(求解器输入文件生成)=====
QStringList loadLines;
for (int i = 0; i < nodes.size(); i++) {
    const Node& n = nodes[i];
    // 生成 Nastran 格式: "FORCE  SID  G  CID  F  N1  N2  N3"
    QString line = QString("FORCE  %1  %2  %3  %4  %5  %6  %7\n")
        .arg(loadSetId,   8)       // SID,8字符宽
        .arg(i + 1,       8)       // Grid ID
        .arg(coordSysId,  8)       // CID
        .arg(n.fx, 8, 'f', 3)     // force X
        .arg(n.fy, 8, 'f', 3)     // force Y
        .arg(n.fz, 8, 'f', 3);    // force Z
    loadLines.append(line);
}
QString bdfContent = loadLines.join("");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

1.3 数值解析——从求解器输出提取数据 ​

cpp
// ===== 解析求解器的科学计数输出 =====
QString line = "MAX_STRESS = 3.4567E+02 MPa";

QStringList parts = line.split('=');
if (parts.size() >= 2) {
    QString numStr = parts[1].trimmed().split(' ')[0];  // "3.4567E+02"
    bool ok = false;
    double value = numStr.toDouble(&ok);
    if (ok) {
        qDebug() << "Max stress:" << value << "MPa";  // 345.67
    } else {
        qWarning() << "Failed to parse stress value:" << numStr;
    }
}

// ===== 批量解析求解器输出表格 =====
// 典型求解器输出:
// NODE    DX          DY          DZ
// 1       0.001234    0.000567    -0.000890
// 2       0.001456    0.000678    -0.000789

QTextStream solverOutput(&file);
while (!solverOutput.atEnd()) {
    QString line = solverOutput.readLine().trimmed();
    if (line.isEmpty() || line.startsWith("NODE")) continue;

    QStringList cols = line.split(QRegularExpression("\\s+"));
    if (cols.size() >= 4) {
        int nodeId = cols[0].toInt();
        double dx = cols[1].toDouble();
        double dy = cols[2].toDouble();
        double dz = cols[3].toDouble();
        // 存入结果数组...
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

1.4 QByteArray——二进制与原始数据 ​

cpp
// ===== 读取二进制网格文件 =====
bool readBinaryMesh(const QString& path, std::vector<double>& coords) {
    QFile file(path);
    if (!file.open(QIODevice::ReadOnly)) return false;

    QByteArray raw = file.readAll();

    if (raw.size() % sizeof(double) != 0) {
        qWarning() << "Corrupted mesh file";
        return false;
    }

    int count = raw.size() / sizeof(double);
    coords.resize(count);
    memcpy(coords.data(), raw.constData(), raw.size());

    return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

1.5 QStringBuilder——高效拼接 ​

cpp
// ===== 大量字符串拼接的性能模式 =====

// ❌ 慢:每一步都创建临时QString
QString result;
for (int i = 0; i < 100000; i++) {
    result += QString("Node %1: (%2, %3, %4)\n")
        .arg(i).arg(x[i]).arg(y[i]).arg(z[i]);
}

// ✅ 快:QStringList + join(一次分配)
QStringList lines;
lines.reserve(100000);
for (int i = 0; i < 100000; i++) {
    lines.append(QString("Node %1: (%2, %3, %4)")
        .arg(i).arg(x[i], 0, 'f', 3).arg(y[i], 0, 'f', 3).arg(z[i], 0, 'f', 3));
}
QString result = lines.join('\n');

// ✅ 也快:QStringBuilder(operator% 链式组合)
#include <QStringBuilder>
result.clear();
for (int i = 0; i < 100000; i++) {
    result += QLatin1String("Node ")
            % QString::number(i)
            % QLatin1String(": (")
            % QString::number(x[i], 'f', 3)
            % QLatin1String(")\n");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

第2部分:QVariant——CAE属性系统的基石 ​

2.1 QVariant的类型擦除机制 ​

cpp
// ===== QVariant 可以存储任意类型的值 =====
QVariant v1 = 42;                    // int
QVariant v2 = 3.14;                  // double
QVariant v3 = "Aluminum";            // QString
QVariant v4 = QPointF(1.0, 2.0);     // QPointF
QVariant v5 = QColor(255, 0, 0);     // QColor
QVariant v6 = QVector3D(1, 0, 0);    // 3D向量

// 类型查询和提取
if (v1.type() == QVariant::Int) {
    int value = v1.toInt();  // 安全提取
}
double d = v2.toDouble();    // 类型转换
1
2
3
4
5
6
7
8
9
10
11
12
13

2.2 自定义类型注册——CAE材料属性 ​

cpp
// ===== Step 1: 定义数据类型 =====
struct MaterialProperty {
    QString name;
    QString unit;       // "MPa", "W/m·K", "kg/m³"
    double minValue;
    double maxValue;
    double defaultValue;
};

// Step 2: 声明为Qt元类型
Q_DECLARE_METATYPE(MaterialProperty)

// Step 3: 注册
void registerCaeTypes() {
    qRegisterMetaType<MaterialProperty>("MaterialProperty");
    // 现在可以在信号槽中传递、存入QVariant
}

// ===== 使用:通用属性编辑器 =====
class MaterialEditor : public QWidget {
    Q_OBJECT
public:
    void setProperty(const QString& key, const QVariant& value) {
        m_properties[key] = value;

        // 根据类型自动选择合适编辑器
        if (value.type() == QVariant::Bool) {
            m_checkBox->setChecked(value.toBool());
        } else if (value.type() == QVariant::Double) {
            m_spinBox->setValue(value.toDouble());
        } else if (value.canConvert<MaterialProperty>()) {
            MaterialProperty prop = value.value<MaterialProperty>();
            // 显示更复杂的属性编辑器...
        }
    }

private:
    QMap<QString, QVariant> m_properties;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

2.3 QVariantMap——CAE JSON配置 ​

cpp
// ===== 材料数据库 =====
QVariantMap aluminum = {
    {"name",           "Aluminum 6061-T6"},
    {"density",        2700.0},
    {"youngs_modulus", 6.89e10},         // Pa
    {"poisson_ratio",  0.33},
    {"yield_strength", 2.76e8},          // Pa
};

// 存储为JSON
QJsonDocument doc = QJsonDocument::fromVariant(aluminum);
QString json = doc.toJson(QJsonDocument::Indented);

// 从JSON恢复
QJsonDocument loaded = QJsonDocument::fromJson(json.toUtf8());
QVariantMap restored = loaded.toVariant().toMap();
double E = restored["youngs_modulus"].toDouble();  // 6.89e10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

2.4 canConvert<T>() 与 value<T>() 的区别 ​

cpp
// ⚠️ value<T>() 不做类型转换!
QVariant v("3.14");          // 字符串
double d1 = v.toDouble();    // 3.14 (toDouble 做隐式转换)
double d2 = v.value<double>(); // 0.0! value<T> 不转换类型

// ✅ 安全转换模式
double safeToDouble(const QVariant& v, double fallback = 0.0) {
    if (v.type() == QVariant::Double || v.type() == QVariant::Int) {
        return v.toDouble();  // 数值类型,直接转
    }
    if (v.type() == QVariant::String) {
        bool ok;
        double d = v.toDouble(&ok);
        return ok ? d : fallback;
    }
    return fallback;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

第3部分:QSettings——配置持久化 ​

3.1 QSettings基本使用 ​

cpp
// ===== 应用级配置 =====
QSettings settings;  // 自动用 QApplication 的 organization/app 名称

// 写
settings.setValue("General/lastProject", "/home/user/project.fcae");
settings.setValue("Viewport/backgroundColor", QColor(30, 30, 30));
settings.setValue("Solver/defaultCores", 4);

// 读(带默认值)
QString lastProject = settings.value("General/lastProject", "").toString();
QColor bgColor = settings.value("Viewport/backgroundColor",
                                 QColor(50, 50, 50)).value<QColor>();
int cores = settings.value("Solver/defaultCores", 2).toInt();

// 用 group 分层
settings.beginGroup("Solver");
settings.setValue("executablePath", "/opt/calculix/ccx");
settings.setValue("maxIterations", 100);
settings.endGroup();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

3.2 CAE项目文件——QSettings INI格式 ​

cpp
// ===== project.fcae = INI文件 =====
// [Project]
// name=WingAnalysis
// solver=Abaqus
// [Mesh]
// file=/path/to/wing.inp
// elementType=C3D8

class ProjectFile {
public:
    bool load(const QString& path) {
        QSettings project(path, QSettings::IniFormat);
        m_name = project.value("Project/name").toString();
        m_solver = project.value("Project/solver").toString();
        m_meshPath = project.value("Mesh/file").toString();

        int size = project.beginReadArray("LoadCases");
        for (int i = 0; i < size; i++) {
            project.setArrayIndex(i);
            LoadCase lc;
            lc.name = project.value("name").toString();
            lc.type = project.value("type").toString();
            m_loadCases.append(lc);
        }
        project.endArray();
        return true;
    }

    bool save(const QString& path) {
        QSettings project(path, QSettings::IniFormat);
        project.setValue("Project/name", m_name);
        project.setValue("Project/solver", m_solver);

        project.beginWriteArray("LoadCases", m_loadCases.size());
        for (int i = 0; i < m_loadCases.size(); i++) {
            project.setArrayIndex(i);
            project.setValue("name", m_loadCases[i].name);
            project.setValue("type", m_loadCases[i].type);
        }
        project.endArray();
        project.sync();  // 强制写入磁盘
        return true;
    }

private:
    QString m_name, m_solver, m_meshPath;
    struct LoadCase { QString name; QString type; };
    QList<LoadCase> m_loadCases;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

第4部分:文件I/O ​

4.1 QFile + QDataStream:二进制序列化 ​

cpp
// ===== CAE网格数据二进制存储 =====
struct MeshBinHeader {
    char magic[4] = {'M', 'E', 'S', 'H'};
    quint32 version = 1;
    quint64 nodeCount;
    quint64 elementCount;
};

bool writeMeshBinary(const QString& path,
                     const std::vector<Node>& nodes,
                     const std::vector<Element>& elems) {
    QFile file(path);
    if (!file.open(QIODevice::WriteOnly)) return false;

    QDataStream stream(&file);
    stream.setVersion(QDataStream::Qt_6_0);
    stream.setByteOrder(QDataStream::LittleEndian);
    stream.setFloatingPointPrecision(QDataStream::DoublePrecision);

    MeshBinHeader header;
    header.nodeCount = nodes.size();
    header.elementCount = elems.size();
    stream.writeRawData(reinterpret_cast<const char*>(&header), sizeof(header));

    stream.writeRawData(reinterpret_cast<const char*>(nodes.data()),
                        nodes.size() * sizeof(Node));
    stream.writeRawData(reinterpret_cast<const char*>(elems.data()),
                        elems.size() * sizeof(Element));

    return stream.status() == QDataStream::Ok;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

4.2 QTextStream:生成Abaqus INP文件 ​

cpp
bool writeAbaqusInput(const QString& path,
                      const std::vector<Node>& nodes,
                      const std::vector<Element>& elems) {
    QFile file(path);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return false;

    QTextStream out(&file);
    out.setRealNumberPrecision(10);
    out.setRealNumberNotation(QTextStream::ScientificNotation);

    out << "*HEADING\nGenerated by FastCAE\n";
    out << "*NODE\n";
    for (size_t i = 0; i < nodes.size(); i++) {
        out << QString("%1, %2, %3, %4\n")
            .arg(i + 1)
            .arg(nodes[i].x, 0, 'E', 10)
            .arg(nodes[i].y, 0, 'E', 10)
            .arg(nodes[i].z, 0, 'E', 10);
    }

    out << QString("*ELEMENT, TYPE=C3D8, ELSET=ALL\n");
    for (size_t i = 0; i < elems.size(); i++) {
        out << QString("%1").arg(i + 1);
        for (int j = 0; j < 8; j++)
            out << QString(", %1").arg(elems[i].nodeIds[j] + 1);
        out << "\n";
    }
    out << "*END\n";
    out.flush();
    return out.status() == QTextStream::Ok;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

第5部分:QProcess——求解器集成 ​

5.1 同步调用 ​

cpp
int runSolverSync(const QString& solverPath, const QStringList& args) {
    QProcess proc;
    proc.start(solverPath, args);

    if (!proc.waitForStarted(5000)) {
        qWarning() << "Failed to start solver";
        return -1;
    }

    if (!proc.waitForFinished(3600000)) {  // 1小时
        qWarning() << "Solver timeout";
        proc.kill();
        return -2;
    }

    return proc.exitCode();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

5.2 异步调用——完整模式 ​

cpp
class SolverRunner : public QObject {
    Q_OBJECT
public:
    void start(const QString& solver, const QStringList& args) {
        m_proc = new QProcess(this);

        connect(m_proc, &QProcess::readyReadStandardOutput,
                this,  &SolverRunner::parseOutput);
        connect(m_proc, &QProcess::readyReadStandardError,
                this,  &SolverRunner::parseErrors);
        connect(m_proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
                this,  &SolverRunner::onFinished);

        m_proc->start(solver, args);
        emit statusChanged("Running...");
    }

    void cancel() {
        if (m_proc && m_proc->state() != QProcess::NotRunning) {
            m_proc->terminate();  // 先礼貌请求退出
            QTimer::singleShot(5000, m_proc, &QProcess::kill);
        }
    }

signals:
    void progressUpdated(int percent, const QString& message);
    void statusChanged(const QString& status);
    void finished(int exitCode);

private slots:
    void parseOutput() {
        QString text = m_proc->readAllStandardOutput();
        // 解析迭代进度...
        static QRegularExpression iterRx(R"(iteration\s+(\d+))");
        auto match = iterRx.match(text);
        if (match.hasMatch()) {
            int iter = match.captured(1).toInt();
            emit progressUpdated(qMin(iter * 100 / 100, 99),
                                QString("Iteration %1").arg(iter));
        }
    }

    void parseErrors() {
        QString text = m_proc->readAllStandardError();
        if (!text.trimmed().isEmpty())
            qWarning() << "Solver error:" << text;
    }

    void onFinished(int code, QProcess::ExitStatus) {
        if (code == 0) emit progressUpdated(100, "Complete");
        emit finished(code);
    }

private:
    QProcess* m_proc = nullptr;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

第6部分:QTimer——仿真步进 ​

6.1 实时仿真驱动 ​

cpp
class SimulationEngine : public QObject {
    Q_OBJECT
public:
    void startRealtimeSim(double dt, double totalTime) {
        m_dt = dt;
        m_currentTime = 0.0;
        m_totalTime = totalTime;

        m_timer = new QTimer(this);
        connect(m_timer, &QTimer::timeout, this, &SimulationEngine::step);
        m_timer->start(static_cast<int>(dt * 1000));
    }

    void pause()  { if (m_timer) m_timer->stop(); }
    void resume() { if (m_timer) m_timer->start(); }

signals:
    void timeStepCompleted(double time, const QVector<double>& field);
    void simulationFinished();

private slots:
    void step() {
        m_currentTime += m_dt;
        auto field = solveOneStep(m_currentTime);
        emit timeStepCompleted(m_currentTime, field);
        if (m_currentTime >= m_totalTime) {
            m_timer->stop();
            emit simulationFinished();
        }
    }

private:
    QTimer* m_timer = nullptr;
    double m_dt, m_currentTime, m_totalTime;
    QVector<double> solveOneStep(double t) { return {}; }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

6.2 singleShot 通用模式 ​

cpp
// 延迟初始化(窗口构建完成后执行)
QTimer::singleShot(0, this, [this]() {
    restoreLastSession();
});

// 防抖输入
void onSearchTextChanged(const QString& text) {
    m_debounceTimer->stop();
    m_debounceTimer->start(300);  // 300ms后触发搜索
}

// 超时看门狗
void startSolver() {
    m_proc->start(solverPath, args);
    QTimer::singleShot(m_timeout * 1000, this, [this]() {
        if (m_proc->state() != QProcess::NotRunning) {
            m_proc->kill();
            emit error("Solver timed out");
        }
    });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

核心总结 ​

总结1:QString处理CAE数据的核心模式 ​

  1. 显示用QString,原始字节用QByteArray,第三方用std::string
  2. 数值格式化用QString::number()或QString().arg(),批量输出用QStringList+join
  3. 解析求解器输出用QRegularExpression(注意Qt6已移除QRegExp)
  4. 大量拼接用QStringList(join)或QStringBuilder(operator%)

总结2:QVariant是CAE属性系统的基石 ​

设计模式:
MaterialEditor 基于 QMap<QString, QVariant>
→ 支持任意材料属性(bool/double/enum/vector...)
→ 新属性类型只需qRegisterMetaType,不用改编辑器
→ QVariantMap ↔ QJsonDocument 无缝互转
1
2
3
4
5

总结3:QProcess 是外部求解器集成的常用方式 ​

对外部命令行求解器,QProcess 提供参数传递、异步输出、状态和退出码管理,通常比 system() 更安全可控。阻塞等待可用于无 GUI 的受控工具,但不能阻塞 GUI 事件线程。取消策略需结合求解器协议:先请求优雅退出、设置可配置超时,再决定是否强制终止;进度可来自结构化 IPC、状态文件或标准输出解析。


章节测试 ​

测试1:QString输出 ​

在Abaqus INP文件中,以下哪种输出格式最合理? A. out << nodes[i].x; (默认输出) B. 每次调用QString::number单独格式化 C. 用QTextStream::setRealNumberNotation(ScientificNotation)设置全局格式后流式输出 D. 用snprintf先格式化到char数组再输出

测试2:QVariant ​

QVariant v("3.14"); 以下哪个描述是错误的? A. v.toDouble() 返回 3.14 B. v.type() 返回 QVariant::String C. v.value<double>() 返回 3.14 D. v.canConvert<double>() 返回 true

测试3:QProcess ​

以下哪种求解器集成方式是正确的? A. UI线程用 proc.waitForFinished(3600000) 等待1小时 B. 子线程用 system("./solver input.inp") 调用 C. 主线程QProcess+信号槽异步,readyReadStandardOutput解析进度 D. 父进程中直接调用求解器的main函数


参考答案 ​

测试1答案 ​

答案:C。用QTextStream的格式控制 + 流式输出最简洁高效。setRealNumberNotation(ScientificNotation)设置一次后,所有数值自动按科学计数格式输出。

测试2答案 ​

答案:C。value<double>()不做类型转换,对于字符串"3.14"它返回0.0(double的默认值)。要用toDouble(&ok)并检查ok值。

测试3答案 ​

答案:C。A阻塞UI;B的system()无进度输出且阻塞;D是进程内调用(没独立进程)。只有C既保持UI响应又能实时解析求解器进度。


相关笔记 ​

  • Signal Slot Deep Dive - 信号槽(QProcess和QTimer都依赖)
  • Object Tree Memory - 对象树(QProcess作为child的生命周期)
  • Solver Basics - 求解器基础

下一步学习 ​

  • [ ] 阅读 04 - Model/View架构
  • [ ] 阅读 05 - 多线程与求解器集成
  • [ ] 用QProcess写一个简单的求解器启动器

学习状态:🟡 开始学习

Qt Core API 的数据流图 ​

text
external bytes/files
 -> QByteArray/QIODevice
 -> explicit decode/parse
 -> QString / value types / domain objects
 -> QVariant/meta-type only at dynamic boundary
 -> models/signals/UI
1
2
3
4
5
6

原则是尽早把不可信动态输入转换为有不变量的领域值,避免在核心逻辑里到处传播 QVariant、QString 编码猜测与裸 JSON object。

API 所有权速查 ​

text
QObject* with parent -> tree-owned
QObject* no parent   -> ownership must be explicit
QString/QByteArray   -> value, often implicitly shared
QStringView/span     -> borrowed view
QIODevice*           -> caller/callee ownership per API, not inferred from pointer
1
2
3
4
5

面试连续追问 ​

问:QVariant 适合所有领域字段吗? 答:不适合。它适合动态属性/模型角色/边界适配;核心领域用具体类型可获得编译检查与明确不变量。

问:隐式共享值按值传递是否永远免费? 答:不是,引用计数有成本,写入 detach 可能深复制,大对象热路径要测量。

问:QIODevice 为什么适合统一文件/网络/缓冲? 答:它提供共同读写接口,但 seek、顺序设备、阻塞和错误语义仍由具体子类决定。

自测与答案 ​

  1. QStringView 能否从临时 QString 长期保存? 答:不能,view 不拥有字符。
  2. QByteArray 是否自动是 UTF-8? 答:不是,编码由边界协议规定。
  3. QVariant 自定义类型跨 queued connection 需要什么? 答:满足元类型/复制移动要求并按 Qt 版本正确声明注册。

最后更新于:

Pager
上一篇3. 对象树与内存管理 / QObject Trees and Memory Management
下一篇5. Model/View架构与CAE数据处理 / Model-View Architecture and CAE Data Processing

持续记录,持续成长

Copyright © Tidenflow