QDataStream with vector of pointers
-
Hi, I am using QDataStream for serialization. I want to use a base class CommandObj and two derived classes AoiCommandObj and CurrentCommandObj in a QVector. That's why I use pointer of CommandObj for polymorphism instead of simple CommandObj instances. Is it right ???
This is my class LightObj:
#ifndef LIGHTOBJ_H #define LIGHTOBJ_H #include <commandobj.h> #include <QObject> #include <QVector> #include <QDateTime> using namespace std; class LightObj { public: LightObj(); ~LightObj(); void setCommandObjs(const QVector<CommandObj*>& commandObjs); QVector<CommandObj*> getCommandObjs() const; private: QVector<CommandObj*> _commandObjs; }; QDataStream &operator<<(QDataStream &out, const LightObj &lightObj); QDataStream &operator>>(QDataStream &in, LightObj &lightObj);
Below is my base class CommonObj.h and .cpp in the QVector:
#ifndef COMMANDOBJ_H #define COMMANDOBJ_H #include <QObject> #include <QDataStream> #include <QDebug> using namespace std; class CommandObj { public: CommandObj(); ~CommandObj(); void setCommandName(const QString& commandName); QString getCommandName() const; private: QString _commandName; }; QDataStream &operator<<(QDataStream &out, const CommandObj *commandObj); QDataStream &operator>>(QDataStream &in, CommandObj *commandObj);
and the CommonObj.cpp:
#include "commandobj.h" CommandObj::CommandObj() { } CommandObj::~CommandObj() { } void CommandObj::setCommandName(const QString& commandName) { this->_commandName = commandName; } QString CommandObj::getCommandName() const { return this->_commandName; } QDataStream &operator<<(QDataStream &out, const CommandObj *commandObj) { out << commandObj->getCommandName() return out; } QDataStream &operator>>(QDataStream &in, CommandObj *commandObj) { qInfo() << "QDataStream &operator>>(QDataStream &in, CommandObj *commandObj)"; QString commandName; in >> commandName; commandObj->setCommandName(commandName); return in; }
Where I do my serialization
LightObj lightObj; lightObj.setCommandObjs(_commandObjs); QFile configFile("ac.dat"); if (configFile.open(QIODevice::WriteOnly)) { QDataStream out(&configFile); out << lightObj; } configFile.close();
The above serialization seems correct without errors. However, I bumped into an segmentation fault while doing deserialization:
QFile configFile("ac.dat"); if (configFile.open(QIODevice::ReadOnly)) { QDataStream configDataStream(&configFile); configDataStream >> _standardLightObj; } else { emit loadConfigFailSignal(fileName); } configFile.close();
The error happens at the following line in CommandObj.cpp:
void CommandObj::setCommandName(const QString& commandName) { this->_commandName = commandName; }
I have no idea about the segmentation fault, did I make any mistake in this architecture? Please help, thanks
-
@VincentLiu You mean you get segmentation fault in this line?
this->_commandName = commandName;
What is _standardLightObj? And was it initialized before doing "configDataStream >> _standardLightObj;"?
It is a pointer, right? Did you assign it a pointer to an CommandObj instance?You should actually use references:
QDataStream &operator>>(QDataStream &in, CommandObj &commandObj)
Note: "this->" is not needed (C++ is not Python :-)).
-
@jsulm Hi, thanks for your reply,
Yes, I get segmentation fault in this line:
this->_commandName = commandName;
I declare the _standardLightObj in a header without using pointer :
LightObj _standardLightObj;
Am I wrong???
I create the QVector<CommandObj*> like this:
QVector<CommandObj*> commandObjs; CommandObj *commandObj = new CommandObj(); CommandObj *aoiCommandObj = new AoiCommandObj(); CommandObj *currentCommandObj = new CurrentCommandObj(); commandObjs.push_back(commandObj); commandObjs.push_back(aoiCommandObj); commandObjs.push_back(currentCommandObj);
I should use the pointer of CommanObj for the polymorphism, right ?
If so, can I still use the reference form ????QDataStream &operator>>(QDataStream &in, CommandObj &commandObj)
Thanks~~~
-
@VincentLiu You can use polymorphism with references as well, but if you put pointers in a vector then you can still use pointers.
-
@VincentLiu said in QDataStream with vector of pointers:
LightObj _standardLightObj
I don't see how it could be incorrect except that you seem to use a global variable which is bad.
Did you try to debug as I suggested before? -
Your problem, almost surely comes from
commandObj->setCommandName(commandName);
with commandObj being null or dangling.A few things:
- Could you show us the implementation of
QDataStream &operator<<(QDataStream &out, const LightObj &lightObj);
andQDataStream &operator>>(QDataStream &in, LightObj &lightObj);
? (i'm 99.99% sure the problem is here) - if AoiCommandObj and CurrentCommandObj have different members to serialise you'll need a more general approach.
- make sure you have clear the concept of
virtual
The usual approach in this case is declare two virtual protected methods in the base class to do the serialisation. something like
class CommandObj{ // other stuff protected: virtual void serialise(QDataStream& stream) const {stream << _commandName;} virtual void deserialise(QDataStream& stream) {stream >> _commandName;} firend QDataStream &operator<<(QDataStream &out, const CommandObj& commandObj); firend QDataStream &operator>>(QDataStream &in, CommandObj& commandObj); }; QDataStream &operator<<(QDataStream &out, const CommandObj& commandObj); QDataStream &operator>>(QDataStream &in, CommandObj& commandObj);
and in the definition:
QDataStream &operator<<(QDataStream &out, const CommandObj& commandObj){commandObj.serialise(out); return out;} QDataStream &operator>>(QDataStream &in, CommandObj& commandObj){commandObj.deserialise(in); return in;}
now in the classes derived from
CommandObj
you just need to overrideserialise
anddeserialise
to make everything work - Could you show us the implementation of
-
@VRonin Thanks. You are right. The commandObj is not accessible in
commandObj->setCommandName(commandName);
when I am under debug mode.Here is my implementation:
QDataStream &operator<<(QDataStream &out, const LightObj &lightObj) { out<< lightObj.getCommandObjs(); return out; } QDataStream &operator>>(QDataStream &in, LightObj &lightObj) { QVector<CommandObj*> commandObjs; in >> commandObjs; lightObj.setCommandObjs(commandObjs); return in; }
I am trying your suggestion now~~~
-
Bingo!
The language/library is not as smart as you think it is. even if
out<< lightObj.getCommandObjs();
might be smart enough to serialise pointers correctly when you usein >> commandObjs;
how can the system know how much memory to allocate and whether aCommandObj*
in the vector is aAoiCommandObj
or aCurrentCommandObj
?You just need to do more manual work
-
QVector<CommandObj*> commandObjs; in >> commandObjs;
Your vector contains pointers and nobody creates instances as far as I can see. You need to read in a loop something like:
while(...) { CommandObj* commandObj = new CommandObj(); in>>commandObj; commandObjs.append(&commandObj); }
-
@VincentLiu While serializing you will need to write type information for each object, so later, when you're de-serializing you know what you're going to read next.
Serializing:- Write type information (could be just a byte)
- Write object
De-serializing:
- Read type information
- Depending on the type create a CommandObj, AoiCommandObj or a CurrentCommandObj object
- Read the object
while( !in.atEnd())...
-
class CommandObj{ public: enum CommandIDs : qint8 { cmdInvalid ,cmdAoiCommand ,cmdCurrentCommand }; CommandObj() :classID(cmdInvalid) {} virtual ~CommandObj()=default; //you probably want a virtual destructor too void setCommandName(const QString& commandName); QString getCommandName() const; qint8 getClassID() const {return classID;} protected: QString _commandName; const qint8 classID; // use this to identify AoiCommandObj from CurrentCommandObj, set it in the constructor virtual void serialise(QDataStream& stream) const {stream << _commandName;} virtual void deserialise(QDataStream& stream) {stream >> _commandName;} firend QDataStream &operator<<(QDataStream &out, const CommandObj& commandObj); firend QDataStream &operator>>(QDataStream &in, CommandObj& commandObj); }; QDataStream &operator<<(QDataStream &out, const CommandObj& commandObj); QDataStream &operator>>(QDataStream &in, CommandObj& commandObj);
QDataStream &operator<<(QDataStream &out, const LightObj &lightObj) { const QVector<CommandObj*> allCmds=lightObj.getCommandObjs(); out<< static_cast<qint32>(allCmds.size()); for(CommandObj* cmd : allCmds) out<< cmd->getClassID() << *cmd; return out; } QDataStream &operator>>(QDataStream &in, LightObj &lightObj) { qint32 vecSize=0; in >>vecSize; qint8 cmdType=CommandObj::cmdInvalid; QVector<CommandObj*> commandObjs; for(int i=0;i<vecSize;++i){ in >>cmdType; CommandObj* tempObj = nullptr; switch(cmdType){ case CommandObj::cmdAoiCommand: tempObj =new AoiCommandObj; break; case CommandObj::cmdCurrentCommand: tempObj = new CurrentCommandObj; break; default: Q_ASSERT_X(false,"QDataStream &operator>>(QDataStream &in, LightObj &lightObj)","Trying to load unsupported command"); } if(!tempObj) continue; in >> *tempObj; commandObjs << tempObj; } lightObj.setCommandObjs(commandObjs); return in; }