我遇到了运营商>>的问题。我试图保存并加载自定义对象的QList文件。保存例程似乎工作正常,但读取文件会导致崩溃。我准备了一个非常小的例子。首先是自定义类:
class CustomObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName)
public:
explicit CustomObject(QObject *parent = 0);
CustomObject(const CustomObject & copy, QObject *parent = 0);
QString name() const;
void setName( const QString & name);
private:
QString m_name;
};
Q_DECLARE_METATYPE( CustomObject )
QDataStream& operator<<( QDataStream& dataStream, const CustomObject * item );
QDataStream& operator>>( QDataStream& dataStream, CustomObject * item );
我用这种方式实现了流操作符:
QDataStream &operator<<(QDataStream &dataStream, const CustomObject *item)
{
for(int i = 0; i < item->metaObject()->propertyCount(); ++i) {
if(item->metaObject()->property(i).isStored(item)) {
dataStream << item->metaObject()->property(i).read(item);
}
}
return dataStream;
}
QDataStream &operator>>(QDataStream &dataStream, CustomObject *item)
{
QVariant var;
for(int i = 0; i < item->metaObject()->propertyCount(); ++i) {
if(item->metaObject()->property(i).isStored(item)) {
dataStream >> var;
item->metaObject()->property(i).write(item, var);
}
}
return dataStream;
}
这是save()
函数(m_objectsList
是QList<CustomObject*>
)
QFile saveFile("/tmp/SaveFile");
if(!saveFile.open(QIODevice::WriteOnly)) {
qWarning() << saveFile.errorString();
return;
}
QDataStream outStream(&saveFile);
outStream.setVersion(QDataStream::Qt_4_8);
outStream << m_objectsList;
saveFile.close();
这是read()
例程:
QFile saveFile("/tmp/SaveFile");
if(!saveFile.open(QIODevice::ReadOnly)) {
qWarning() << saveFile.errorString();
return;
}
QDataStream inStream(&saveFile);
inStream >> m_objectsList;
saveFile.close();
应用程序在操作符&gt;&gt;:
中的for循环的条件语句中进行了段错误i < item->metaObject()->propertyCount()
item
无法访问。
你能解释一下错误在哪里吗?
非常感谢。
答案 0 :(得分:1)
您的代码段错误,因为item
是一个悬空指针。没有人初始化它。在您阅读之前,您可以实例化item
。
您可能还应该尝试存储动态属性,并确保至少有一些向后兼容的可能性。
下面的代码提供了两个模板函数,用于序列化对象列表(由其属性定义)。首先,让我们重新迭代对象类型:
// https://github.com/KubaO/stackoverflown/tree/master/questions/prop-storage-24185694
#include <QtCore>
class CustomObject : public QObject {
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName STORED true)
QString m_name;
public:
#ifdef Q_MOC_RUN
Q_INVOKABLE CustomObject(QObject *parent = {})
#endif
using QObject::QObject;
QString name() const { return m_name; }
void setName(const QString &name) { m_name = name; }
};
一些助手:
/// Returns a zero-copy byte array wrapping a C string constant
static QByteArray baFromCStr(const char *str) {
return QByteArray::fromRawData(str, qstrlen(str));
}
/// Returns a list of stored properties for a given type
QList<QMetaProperty> storedProperties(const QMetaObject *mo) {
QList<QMetaProperty> stored;
for (int i = 0; i < mo->propertyCount(); ++i) {
auto prop = mo->property(i);
if (prop.isStored())
stored << prop;
}
return stored;
}
/// Caches strings for saving to a data stream
struct SaveCache {
QMap<QByteArray, qint32> strings;
QDataStream &save(QDataStream &str, const QByteArray &string) {
auto it = strings.find(string);
if (it != strings.end())
return str << (qint32)it.value();
auto key = strings.count();
strings.insert(string, key);
return str << (qint32)key << string;
}
QDataStream &save(QDataStream &str, const char *string) {
return save(str, baFromCStr(string));
}
};
/// Caches strings while loading from a data stream
struct LoadCache {
QList<QByteArray> strings;
QDataStream &load(QDataStream &str, QByteArray &string) {
qint32 index;
str >> index;
if (index >= strings.count()) {
str >> string;
while (strings.size() < index)
strings << QByteArray{};
strings << string;
} else
string = strings.at(index);
return str;
}
};
存储在流中的格式可以描述如下:
template <typename T>
QDataStream &writeObjectList(QDataStream &str, const QList<T*> &items) {
str << (quint32)items.count();
if (! items.count()) return str;
str << (quint8)1; // version
SaveCache strings;
for (QObject *item : items) {
auto *mo = item->metaObject();
// Type
strings.save(str, mo->className());
// Properties
auto const stored = storedProperties(mo);
auto const dynamic = item->dynamicPropertyNames();
str << (quint32)(stored.count() + dynamic.count());
for (auto &prop : qAsConst(stored))
strings.save(str, prop.name()) << prop.read(item);
for (auto &name : dynamic)
strings.save(str, name) << item->property(name);
}
return str;
}
读取方法必须尝试两种实例化对象的方法:
template <typename T> QDataStream &readObjectList(QDataStream &str,
QList<T*> &items)
{
quint32 itemCount;
str >> itemCount;
if (!itemCount) return str;
quint8 version;
str >> version;
if (version != 1) {
str.setStatus(QDataStream::ReadCorruptData);
return str;
}
LoadCache strings;
for (; itemCount; itemCount--) {
QByteArray string;
// Type
T *obj = {};
strings.load(str, string);
if (T::staticMetaObject.className() == string)
obj = new T();
else {
string.append('*');
auto id = QMetaType::type(string);
const auto *mo = QMetaType::metaObjectForType(id);
if (mo)
obj = qobject_cast<T*>(mo->newInstance());
}
if (obj)
items << obj;
// Properties
quint32 propertyCount;
str >> propertyCount;
for (uint i = 0; i < propertyCount; ++i) {
QVariant value;
strings.load(str, string) >> value;
if (obj) obj->setProperty(string, value);
}
}
return str;
}
一个非常简单的测试工具:
QDataStream &operator<<(QDataStream &str, const QList<CustomObject*> &items) {
return writeObjectList(str, items);
}
QDataStream& operator>>(QDataStream &str, QList<CustomObject*> &items) {
return readObjectList(str, items);
}
int main() {
qRegisterMetaType<CustomObject*>(); // necessary for any classes derived from CustomObject*
QBuffer buf;
buf.open(QBuffer::ReadWrite);
QDataStream ds(&buf);
CustomObject obj;
obj.setObjectName("customsky");
obj.setProperty("prop", 20);
QList<CustomObject*> list;
list << &obj;
ds << list;
QList<CustomObject*> list2;
buf.seek(0);
ds >> list2;
Q_ASSERT(list2.size() == list.size());
for (int i = 0; i < list.size(); ++i) {
auto *obj1 = list.at(i);
auto *obj2 = list2.at(i);
Q_ASSERT(obj1->objectName() == obj2->objectName());
Q_ASSERT(obj1->dynamicPropertyNames() == obj2->dynamicPropertyNames());
for (auto &name : obj1->dynamicPropertyNames())
Q_ASSERT(obj1->property(name) == obj2->property(name));
}
}
#include "main.moc"
答案 1 :(得分:1)
您的代码崩溃了,因为在您的输入操作符中item
指针实际上并不指向对象,并且可能为null。要解决这个问题,操作符应该引用指针,并创建一个新的CustomObject()实例。像这样:
QDataStream &operator>>(QDataStream &dataStream, CustomObject *& item)
{
QVariant var;
item = new CustomObject();
for(int i = 0; i < item->metaObject()->propertyCount(); ++i) {
if(item->metaObject()->property(i).isStored(item)) {
dataStream >> var;
item->metaObject()->property(i).write(item, var);
}
}
return dataStream;
}