Skip to content
正常

QIODevice 抽象基类与 QFile/QSaveFile 源码索引

本索引收录 Qt 6.9.1 源码中 QIODevice 抽象基类 + QFile/QFileDevice/QSaveFile 的已验证证据。QIODevice 是 Qt 所有 IO 类(QFile/QBuffer/QProcess/QAbstractSocket/QTcpSocket)的共同基类。

QIODevice 双继承与 OpenMode 标志

源码文件:qtbase/src/corelib/io/qiodevice.h / qiodevicebase.h

论点行号原文摘要解读
双继承 QObject + QIODeviceBaseqiodevice.h:30-37class QIODevice : public QObject, public QIODeviceBase(带 #ifndef QT_NO_QOBJECT 分支)QIODeviceBase 是 Qt6 拆出的非模板纯数据基类(析构 protected),只放 OpenMode,避免循环依赖。QObject 可用 QT_NO_QOBJECT 编译关掉。
OpenMode 10 个位标志全在 QIODeviceBaseqiodevicebase.h:16-28enum OpenModeFlag { NotOpen=0x0000, ReadOnly=0x0001, WriteOnly=0x0002, ReadWrite=ReadOnly|WriteOnly, Append=0x0004, Truncate=0x0008, Text=0x0010, Unbuffered=0x0020, NewOnly=0x0040, ExistingOnly=0x0080 }; Q_DECLARE_FLAGS(OpenMode, OpenModeFlag)ReadWrite 是位运算组合(值 0x3)。Text/Unbuffered/NewOnly/ExistingOnly 是修饰位。集中在 QIODeviceBase 印证拆基类动机。

双模式与 seek 守卫

源码文件:qtbase/src/corelib/io/qiodevice.cpp

论点行号原文摘要解读
isSequential 默认 false(随机访问):493-496bool QIODevice::isSequential() const { return false; }QFileDevice 覆盖它委托 fileEngine->isSequential()——普通文件返 false,管道/串口等返 true。正文别泛化「QFileDevice 永远 false」。
seek 三道守卫 + devicePos/seekBuffer:857-886if (d->isSequential()) { checkWarnMessage(...); return false; } if (d->openMode == NotOpen) {...} if (pos < 0) {...} ... d->devicePos = pos; d->seekBuffer(pos); return true;sequential 设备 seek 直接 false(带警告);通过后更新 devicePos 并 seekBuffer(清/截 buffer)。

readData/writeData 纯虚契约(子类核心)

源码文件:qtbase/src/corelib/io/qiodevice.h / qiodevice.cpp

论点行号原文摘要解读
readData 是纯虚 = 0qiodevice.h:132virtual qint64 readData(char *data, qint64 maxlen) = 0;不是「默认返回 -1」(那是 Qt4/5 旧印象)。QIODevice 是抽象类,qiodevice.cpp 仅 \fn doc 无函数体;子类 QFileDevice::readData(qfiledevice.cpp:462)才是真身。
writeData 也是纯虚 = 0qiodevice.h:135virtual qint64 writeData(const char *data, qint64 len) = 0;同 readData。write() 主路径调 writeData,Win 平台 #ifdef Q_OS_WIN 的 Text 模式按块扫描 \n 分次 writeData 做 \n→\r\n 转换(仅 Win)。
read 公共薄壳 + getChar 快捷qiodevice.cpp:996-1039CHECK_READABLE(read, qint64(-1)); if (maxSize == 1 && ...) { while ((chint = d->buffer.getChar()) != -1) {...} } ... const qint64 readBytes = d->read(data, maxSize);maxSize==1 的 getChar 快捷路径仍调 readData(data,0) 预填缓冲(非绕过);真逻辑在 QIODevicePrivate::read。Text 模式吃掉 \r

QRingBuffer 缓冲与两路径 read

源码文件:qtbase/src/corelib/io/qiodevice_p.h / qiodevice.cpp

论点行号原文摘要解读
缓冲结构:QRingBufferRef(引用)+ QVarLengthArray(拥有)+ chunkSize 16384qiodevice_p.h:105-116, :30-31QRingBufferRef buffer; QRingBufferRef writeBuffer; int readBufferChunkSize = QIODEVICE_BUFFERSIZE; QVarLengthArray<QRingBuffer, 2> readBuffers; QVarLengthArray<QRingBuffer, 1> writeBuffers;#define QIODEVICE_BUFFERSIZE 16384QRingBufferRef 持裸指针 m_buf(引用非拥有),真实所有权在 readBuffers/writeBuffers。setCurrentReadChannel 把 &readBuffers[channel] 挂到 buffer.m_buf。
read 两路径:大块直读 vs 填充缓冲qiodevice.cpp:1048, :1082-1116const bool buffered = (readBufferChunkSize != 0 && (openMode & Unbuffered) == 0); if ((!buffered || maxSize >= readBufferChunkSize) && !keepDataInBuffer) readFromDevice = q->readData(data, maxSize); else readFromDevice = q->readData(buffer.reserve(bytesToBuffer), bytesToBuffer);maxSize>=16KB 或 Unbuffered → 直读用户缓冲绕过 ring;小读 → 先填 ring,下次 read 命中 buffer。

readLine 与 Text 模式

源码文件:qtbase/src/corelib/io/qiodevice.cpp

论点行号原文摘要解读
readLine 先 peek buffer 再回退 readLineData;Text 出口做 \r\n→\n:1346-1410, :1622-1644if (!buffer.isEmpty()) readSoFar = buffer.readLine(...); if (data[readSoFar-1]=='\n') { if (openMode & Text) { if (data[readSoFar-2]=='\r') { --readSoFar; data[readSoFar-1]='\n'; } } return readSoFar; } / readLineData while (...) { *data++ = c; if (c == '\n') break; }buffer 里有 \n 就 peek 出来避免 readData;不够回退 readLineData(默认逐字节 read,性能差,QFileDevice 可 override 优化)。Text 转换在 readLine 出口。

信号契约(谁发射)

源码文件:qtbase/src/corelib/io/qiodevice.h / qiodevice.cpp

论点行号原文摘要解读
readyRead/bytesWritten 由子类 emit,QIODevice 自己不 emit:118, :120void readyRead(); void bytesWritten(qint64 bytes);(Q_SIGNALS 块)信号声明在 QIODevice,但 grep qiodevice.cpp 无 emit readyRead/bytesWritten(仅 doc 提及)。由 QAbstractSocket 等子类在 OS notify 回调里发射。异步 IO 驱动契约。
aboutToClose 是 QIODevice 唯一自 emit 的信号:122 + qiodevice.cpp:787-807void aboutToClose(); / close() 里 ... emit aboutToClose(); d->openMode = NotOpen;在 close() 改 openMode 之前发射,让订阅者能 flush 等。writeBuffers 不清空(为 socket delayed close 留尾巴)。

QFile→QFileDevice→FileEngine 三层与平台 open

源码文件:qtbase/src/corelib/io/qfile.h / qfiledevice.h / qfiledevice_p.h / qfile.cpp / qfsfileengine_unix.cpp / qfsfileengine_win.cpp

论点行号原文摘要解读
三层继承链qfile.h:92, qfiledevice.h:31class QFile : public QFileDevice / class QFileDevice : public QIODeviceQFileDevice 是 Qt5.0 拆出的中间层(文件句柄封装),让 QTemporaryFile/QSaveFile 共享。
QFileDevicePrivate 持 FileEngine 不持 fdqfiledevice_p.h:58mutable std::unique_ptr<QAbstractFileEngine> fileEngine;全文无 FILE* fh 或 int fd 字段。真 fd 在 QFSFileEnginePrivate(unix)、fileHandle(win)。推翻「QFile 直接封装 fd」误解。
QFile::open 强制 Unbuffered + engine 调平台 nativeqfile.cpp:931-951, qfsfileengine_unix.cpp:35-60, :88-96if (d->engine()->open(mode | QIODevice::Unbuffered)) {...} / Q_ASSERT_X(openMode & Unbuffered, ...); fd = QT_OPEN(...); / Win fileHandle = CreateFile(...)「QIODevice 做 buffering,engine 只做裸 IO」——QFSFileEngine 有断言不再支持 buffered。Unix 走 QT_OPEN(POSIX open,非 stdio fopen),Win 走 CreateFile。

QSaveFile 原子写

源码文件:qtbase/src/corelib/io/qsavefile.h / qsavefile.cpp

论点行号原文摘要解读
close 私有 + qFatal 硬阻止qsavefile.h:53-54, qsavefile.cpp:272-274private: void close() override; / void QSaveFile::close() { qFatal("QSaveFile::close called"); }强制必须走 commit,不能误调普通 close。
commit 四件套:flush→syncToDisk→renameOverwriteqsavefile.cpp:289-330QFileDevice::close(); fe->syncToDisk(); if (d->useTemporaryFile) { if (d->error != NoError) { fe->remove(); return false; } if (!fe->renameOverwrite(d->finalFileName)) { fe->remove(); return false; } }flush + fsync + 无错原子 rename 替换 / 有错 remove 丢弃。
writeData 捕获错误存 writeErrorqsavefile.cpp:362-373if (d->writeError != NoError) return -1; const qint64 ret = QFileDevice::writeData(data, len); if (d->error != NoError) d->writeError = d->error;一次写失败后后续 write 直接返 -1,commit 时据 writeError 决定丢弃。

AwesomeQt v0.3.0-13-g31f4d47 · 31f4d47 · 2026-09-22 · CHECKED-BY: CI