【问题标题】:QDataStream QIODevice memory allocationQDataStream QIODevice 内存分配
【发布时间】:2012-10-13 22:13:59
【问题描述】:

假设我有一个创建 QIODevice(例如 QFile)的函数,然后返回一个指向从 QIODevice 构造的 QDataStream 的指针。在这里处理内存分配的最佳方法是什么?显然,QIODevice 必须在函数终止时进行堆分配以保持对 QDataStream 可用,但是 QDataStream 的销毁不会破坏或关闭设备。有没有标准的方法来处理这个看似常见的问题? 理想情况下,我想要一个返回对象(不是指向对象的指针)的函数,该对象的行为类似于 QDataStream 但在销毁时会关闭设备。实际上是标准库输入流。

示例代码:

QDataStream* getStream(const QString& filename) {
  QFile* file = new QFile(filename); // needs to be explicitly deleted later
  file->open(QIODevice::ReadOnly);
  QDataStream* out = new QDataStream(&file); // same here
  return out;
}

【问题讨论】:

    标签: c++ qt


    【解决方案1】:
    std::shared_ptr<QDataStream> getStream(const QString& filename) 
    {
      QFile* file = new QFile(filename); // needs to be explicitly deleted later
      file->open(QIODevice::ReadOnly);
      std:shared_ptr<QDataStream> out(new QDataStream(&file), QDSDeleter);
      return out;
    }
    
    void QDSDeleter(QDataStream* s)
    {
       QIODevice* device = s->device();
       device->close();
       delete device;
    }
    

    std::unique_ptr 是另一种选择,具体取决于您的需要; here's a reference 前者,如果你需要的话。

    编辑:Qt 的QSharedPointer 类也有此功能,您还可以在其中提供删除器作为构造函数参数。那里给出了其他指针包装器选项。谢谢@RA。进行更正。

    【讨论】:

    • 我想你的意思是QSharedPointer(QScopedPointer不能被复制)。
    【解决方案2】:

    QDataStream 有一个方便的 owndev 私有成员,您可以将其设置为 true 以使流有效地拥有设备。流也可以很容易地移动 - 使其非常接近您对它表现得像一个值的要求。理想情况下,您可以修改您的 Qt 副本来实现它,但也可以解决它。

    // https://github.com/KubaO/stackoverflown/tree/master/questions/qdatastream-move-own-13039614
    #include <QDataStream>
    
    class DataStream : public QDataStream {
       struct Proxy {
          QScopedPointer<QDataStreamPrivate> d;
          QIODevice *dev;
          bool owndev;
          bool noswap;
          QDataStream::ByteOrder byteorder;
          int ver;
          QDataStream::Status q_status;
    #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
          virtual ~Proxy();
    #endif
       };
       static Proxy *p(QDataStream *ds) { return reinterpret_cast<Proxy *>(ds); }
       static const Proxy *p(const QDataStream *ds) {
          return reinterpret_cast<const Proxy *>(ds);
       }
    #if defined(QT_TESTLIB_LIB) || defined(QT_MODULE_TEST)
       friend class DataStreamTest;
    #endif
      public:
       DataStream() = default;
       using QDataStream::QDataStream;
       DataStream(DataStream &&other) : DataStream(static_cast<QDataStream &&>(other)) {}
       DataStream(QDataStream &&other) {
          using std::swap;
          Proxy &o = *p(&other);
          Proxy &t = *p(this);
          swap(t.d, o.d);
          swap(t.dev, o.dev);
          swap(t.owndev, o.owndev);
          swap(t.noswap, o.noswap);
          swap(t.byteorder, o.byteorder);
          swap(t.ver, o.ver);
          swap(t.q_status, o.q_status);
       }
       DataStream &operator=(DataStream &&other) {
          return *this = static_cast<QDataStream &&>(other);
       }
       DataStream &operator=(QDataStream &&other) {
          this->~DataStream();
          new (this) DataStream(std::move(other));
          return *this;
       }
       void setOwnedDevice(QIODevice *dev) {
          setDevice(dev);
          p(this)->owndev = true;
       }
       bool ownsDevice() const { return p(this)->owndev; }
       static bool ownsDevice(const QDataStream *ds) { return p(ds)->owndev; }
    };
    

    由于QObject 有一个内置的引用计数,如果我们愿意,我们也可以让QDataStream 作为它的共享指针。

    在 Qt 4.8 和 5.10 上测试了以下要求:

    PASS   : DataStreamTest::isBinaryCompatible()
    PASS   : DataStreamTest::streams()
    PASS   : DataStreamTest::movesFromNotOwnedQDataStream()
    PASS   : DataStreamTest::movesFromNotOwnedDataStream()
    PASS   : DataStreamTest::assignsFromNotOwnedQDataStream()
    PASS   : DataStreamTest::assignsFromNotOwnedDataStream()
    PASS   : DataStreamTest::returnsFromNotOwnedQDataStream()
    PASS   : DataStreamTest::returnsFromNotOwnedDataStream()
    PASS   : DataStreamTest::movesFromOwnedQDataStream()
    PASS   : DataStreamTest::moveFromOwnedDataStream()
    PASS   : DataStreamTest::assignsFromOwnedQDataStream()
    PASS   : DataStreamTest::assignsFromOwnedDataStream()
    PASS   : DataStreamTest::returnsFromOwnedQDataStream()
    PASS   : DataStreamTest::returnsFromOwnedDataStream()
    

    测试套件如下。二进制兼容性测试非常广泛,几乎排除了我们所依赖的 UB 存在问题的可能性。请注意,QDataStream 的布局在主要 Qt 版本中无法更改 - 因此上述代码将适用于所有未来的 Qt 5 版本。

    #include <QtTest>
    
    class DataStreamTest : public QObject {
       Q_OBJECT
       static QObjectData *getD(QObject *obj) {
          return static_cast<DataStreamTest *>(obj)->d_ptr.data();
       }
       static bool wasDeleted(QObject *obj) { return getD(obj)->wasDeleted; }
       template <typename T, typename... Args>
       DataStream make_stream(Args &&... args) {
          return T(std::forward<Args>(args)...);
       }
       static QDataStream::ByteOrder flipped(QDataStream::ByteOrder o) {
          return (o == QDataStream::BigEndian) ? QDataStream::LittleEndian
                                               : QDataStream::BigEndian;
       }
       Q_SLOT void isBinaryCompatible() {
          QCOMPARE(sizeof(DataStream), sizeof(QDataStream));
          QCOMPARE(sizeof(DataStream::Proxy), sizeof(QDataStream));
          struct Test {
             QByteArray data;
             QDataStream ds{&data, QIODevice::ReadWrite};
             void check(int loc = 0) {
                if (!loc) {
                   check(1);
                   ds.setDevice(nullptr);
                   check(1);
                }
                QCOMPARE(!!ds.device(), DataStream::ownsDevice(&ds));
                QCOMPARE(ds.device(), DataStream::p(&ds)->dev);
    
                if (!loc) check(2);
                bool noswap = DataStream::p(&ds)->noswap;
                QCOMPARE(noswap, DataStream::p(&ds)->noswap);
                QCOMPARE(ds.byteOrder(), DataStream::p(&ds)->byteorder);
                if (loc != 2) {
                   ds.setByteOrder(flipped(ds.byteOrder()));
                   noswap = !noswap;
                }
                if (!loc) check(2);
                QCOMPARE(noswap, DataStream::p(&ds)->noswap);
    
                if (!loc) check(3);
                QCOMPARE(ds.version(), DataStream::p(&ds)->ver);
                if (loc != 3) ds.setVersion(QDataStream::Qt_4_0);
                if (!loc) check(3);
    
                if (!loc) check(4);
                QCOMPARE(ds.status(), DataStream::p(&ds)->q_status);
                if (loc != 4) ds.setStatus(QDataStream::ReadPastEnd);
                if (!loc) check(4);
             }
          } test;
          test.check();
       }
       Q_SLOT void streams() {
          QString str{"Hello, world"};
          QVector<uint> ints{44, 0xDEADBEEF, 1};
          QByteArray data;
          DataStream ds(&data, QIODevice::ReadWrite);
          ds << str << ints;
          ds.device()->reset();
          QString str2;
          QVector<uint> ints2;
          ds >> str2 >> ints2;
          QCOMPARE(str2, str);
          QCOMPARE(ints2, ints);
       }
       Q_SLOT void movesFromNotOwnedQDataStream() {
          QBuffer buf;
          QDataStream ds(&buf);
          QVERIFY(ds.device() == &buf);
          DataStream ds2(std::move(ds));
          QVERIFY(!ds.device());
          QVERIFY(ds2.device() == &buf);
          QVERIFY(!wasDeleted(&buf));
       }
       Q_SLOT void movesFromNotOwnedDataStream() {
          QBuffer buf;
          DataStream ds(&buf);
          QVERIFY(ds.device() == &buf);
          DataStream ds2(std::move(ds));
          QVERIFY(!ds.device());
          QVERIFY(ds2.device() == &buf);
          QVERIFY(!wasDeleted(&buf));
       }
       Q_SLOT void assignsFromNotOwnedQDataStream() {
          QBuffer buf;
          QDataStream ds(&buf);
          QVERIFY(ds.device() == &buf);
          DataStream ds2;
          ds2 = std::move(ds);
          QVERIFY(!ds.device());
          QVERIFY(ds2.device() == &buf);
          QVERIFY(!wasDeleted(&buf));
       }
       Q_SLOT void assignsFromNotOwnedDataStream() {
          QBuffer buf;
          DataStream ds(&buf);
          QVERIFY(ds.device() == &buf);
          DataStream ds2;
          ds2 = std::move(ds);
          QVERIFY(!ds.device());
          QVERIFY(ds2.device() == &buf);
          QVERIFY(!wasDeleted(&buf));
       }
       Q_SLOT void returnsFromNotOwnedQDataStream() {
          QBuffer buf;
          {
             auto ds = make_stream<QDataStream>(&buf);
             QVERIFY(ds.device());
             QVERIFY(!ds.ownsDevice());
          }
          QVERIFY(!wasDeleted(&buf));
       }
       Q_SLOT void returnsFromNotOwnedDataStream() {
          QBuffer buf;
          buf.open(QIODevice::ReadWrite);
          {
             auto ds = make_stream<DataStream>(&buf);
             QVERIFY(ds.device());
             QVERIFY(!ds.ownsDevice());
          }
          QVERIFY(!wasDeleted(&buf));
       }
       Q_SLOT void movesFromOwnedQDataStream() {
          QPointer<QIODevice> buf;
          {
             QByteArray data;
             QDataStream ds(&data, QIODevice::ReadWrite);
             QVERIFY(DataStream::ownsDevice(&ds));
             buf = ds.device();
             DataStream ds2(std::move(ds));
             QVERIFY(!ds.device());
             QVERIFY(ds2.device() == buf);
             QVERIFY(buf);
          }
          QVERIFY(!buf);
       }
       Q_SLOT void moveFromOwnedDataStream() {
          QPointer<QBuffer> buf(new QBuffer);
          {
             DataStream ds;
             ds.setOwnedDevice(buf);
             QVERIFY(ds.device() == buf);
             DataStream ds2(std::move(ds));
             QVERIFY(!ds.device());
             QVERIFY(ds2.device() == buf);
             QVERIFY(buf);
          }
          QVERIFY(!buf);
       }
       Q_SLOT void assignsFromOwnedQDataStream() {
          QPointer<QIODevice> buf;
          {
             QByteArray data;
             QDataStream ds(&data, QIODevice::ReadWrite);
             QVERIFY(DataStream::ownsDevice(&ds));
             buf = ds.device();
             DataStream ds2;
             ds2 = std::move(ds);
             QVERIFY(!ds.device());
             QVERIFY(ds2.device() == buf);
             QVERIFY(buf);
          }
          QVERIFY(!buf);
       }
       Q_SLOT void assignsFromOwnedDataStream() {
          QPointer<QBuffer> buf(new QBuffer);
          {
             DataStream ds;
             ds.setOwnedDevice(buf);
             QVERIFY(ds.device() == buf);
             DataStream ds2;
             ds2 = std::move(ds);
             QVERIFY(!ds.device());
             QVERIFY(ds2.device() == buf);
             QVERIFY(buf);
          }
          QVERIFY(!buf);
       }
       Q_SLOT void returnsFromOwnedQDataStream() {
          QPointer<QIODevice> dev;
          QByteArray data;
          {
             auto ds = make_stream<QDataStream>(&data, QIODevice::ReadWrite);
             dev = ds.device();
             QVERIFY(ds.device());
             QVERIFY(ds.ownsDevice());
          }
          QVERIFY(!dev);
       }
       Q_SLOT void returnsFromOwnedDataStream() {
          QPointer<QIODevice> dev;
          QByteArray data;
          {
             auto ds = make_stream<DataStream>(&data, QIODevice::ReadWrite);
             dev = ds.device();
             QVERIFY(ds.device());
             QVERIFY(ds.ownsDevice());
          }
          QVERIFY(!dev);
       }
    };
    
    QTEST_MAIN(DataStreamTest)
    #include "main.moc"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多