QDataStream QIODevice内存分配

时间:2012-10-23 21:32:05

标签: c++ qt

假设我有一个创建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;
}

2 个答案:

答案 0 :(得分:5)

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。为了纠正。

答案 1 :(得分:0)

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"