How to create a QMap of objects?



  • How can I achieve having a QMap of objects something like below and then be able to access them and pass them to other objects? I keep running into issues due to "const" values.

    @
    QMap<QString,MyClass> myObjs;
    quint objTotal = 8;

    for (int i=0; i < objTotal; i++) {
    MyClass mc("paramA" + QString::number(i),"paramB" + QString::number(i),10);
    myObjs.insert("paramA" + QString::number(i),mc);
    }

    qDebug() << myObjs.value("paramA1").showData();
    @

    myclass.cpp
    @
    MyClass::MyClass(const QString &param1, const QString &param2, quint8 &DEBUG) {
    this->param1 = param1;
    this->param2 = param2;
    this->DEBUG = DEBUG;
    }

    void MyClass::showData() {
    qDebug() << param1 << param2 << DEBUG;
    }
    @



  • The value method return "const data type":http://doc.qt.nokia.com/4.7-snapshot/qmap.html#value:
    @const T value ( const Key & key ) const@
    Use
    @myObjs["paramA1"]@
    and read the "docs":http://doc.qt.nokia.com/4.7-snapshot/qmap.html

    And righter to make thе showData method constant.



  • Sorry, I typed the last line of the main block wrong. The "qDebug() << " shouldn't have made it in. But regardless, the code still doesn't compile with the changes Maksim suggested.

    I have read the docs, but I can't find anything there regarding errors that show up and don't show the line of user code that matches the error. Instead, QT Creator is giving me a more general error from the qmap header file:

    Updated main block:
    @
    QMap<QString,MyClass> myObjs;
    quint objTotal = 8;

    for (int i=0; i < objTotal; i++) {
    MyClass mc("paramA" + QString::number(i),"paramB" + QString::number(i),10);
    myObjs["paramA" + QString::number(i)] = mc;
    }

    qDebug() << myObjs["paramA1"].showData();
    @

    @
    no matching function for call to MyClass::MyClass() qmap.h 531
    candidates are: MyClass(const QString&, const QString&, quint8&)
    MyClass::MyClass(const MyClass&)
    @

    Now I understand what the "no matching function for call..." is trying to tell me. It just doesn't make sense why I'm getting that message. With the info I have, I'm no closer to understanding how to make a MAP of objects work.



  • This line is really suspect:
    qDebug() << myObjs["paramA1"].showData();

    Same for:
    qDebug() << myObjs.value("paramA1").showData();

    In both call you use a function returning 'void' (i.e. showData()). And the second problem with value() it returns a const instance which means that you should declare: void MyClass::showData() const;



  • I forgot to take it out again. Bad me! I hand typed it in to the post. It's not in my working code.

    [quote author="BilbonSacquet" date="1321389819"]This line is really suspect:
    qDebug() << myObjs["paramA1"].showData();

    Same for:
    qDebug() << myObjs.value("paramA1").showData();

    In both call you use a function returning 'void' (i.e. showData()). And the second problem with value() it returns a const instance which means that you should declare: void MyClass::showData() const;[/quote]



  • Ok. It has something to do with assigning the parameters in the Constructor of MyClass. If I create a separate function to assign the private variables, I can get it to work.

    My worry has been that I didn't want to have an instance of MyClass accessed without the default parameters set. That would predicate the need to have the "assignment" function run as soon as possible.



  • Go to the last word of my post, add simply a 'const' at the end of your 'showData()' function! :)
    @void showData() const {}@
    Or if you want to do a sin, use const_cast<>() ... :P



  • The Qt container classes work on assignable types.

    bq. From the "Qt Container classes docs":http://developer.qt.nokia.com/doc/qt-4.7/containers.html [emphasis by me]
    The values stored in the various containers can be of any assignable data type. To qualify, a type must provide a default constructor, a copy constructor, and an assignment operator. This covers most data types you are likely to want to store in a container, including basic types such as and , pointer types, and Qt data types such as QString, QDate, and QTime, but it doesn't cover QObject or any QObject subclass (QWidget, QDialog, QTimer, etc.). If you attempt to instantiate a QList<QWidget>, the compiler will complain that QWidget's copy constructor and assignment operators are disabled. If you want to store these kinds of objects in a container, store them as pointers, for example as QList<QWidget *>.

    So in your case, you must provide:

    • the default constructor MyClass(), or add default values to your parametrized constructor
    • a copy constructor MyClass(const MyClass &other)
    • an assignment operator MyClass &operator=(const MyClass &other)


  • Ah! There's the answer I was looking for and couldn't find on my own! Thank you Volker!!!



  • Another solution would be to use pointers, instead of values. This way you would not need the additional constructors and assignment ops.



  • I thought I tried to use a pointer, but I got a different error. I'll try it because of a new wrinkle.

    On that note, is it true that because I'm NOT using a pointer for "myObjs" I lose all the values when the program goes out of scope of the "assignment" function? I'm noticing that the structure of the class values are there, but the data assigned in the "for" loop in "MyClass" is empty.



  • Yes, it's true. If you want to use objects outside of the scope, you should create them with new



  • Ok. Here's where I hit the snag now. It all compiles fine, then I get a Segmentation fault. I just can't see the reason.

    ObjsClass header file:
    @
    public:
    ObjsClass();
    void showData(QString&);

    private:
    QMap<QString,MyClass> *myObjs;

    @

    ObjsClass cpp file:
    @
    #include "objsclass.h"

    ObjsClass::ObjsClass()
    {
    QMap<QString,MyClass> localObjs;

    for (int i = 0; i < 8; i++) {
        MyClass mc;
        mc.assignVals("paramA" + QString::number(i),"paramB" + QString::number(i),10);
        localObjs["paramA" + QString::number(i)] = mc;
    }
    
    myObjs = &localObjs;
    

    }

    void ObjsClass::showData(QString &index) {
    (*myObjs)[index].showData();
    }
    @



  • It's not clear what goes out of scope in your program.

    The myObjs object is alive as long as your instance of the ObjsClass class is alive. If you use a pointer for the map, you must create it with new:

    @
    myObjs = new QMap<QString, MyClass>;
    @

    You're doing it wrong™ in your constructor:
    You create a local object localObjs on the stack in line 5. Then, in line 13, you assign the address of this local object to the myObjs pointer. But as soon as the constructor is done (line 14), the local localObjs object is destroyed. myObjs is still pointing to that no more valid object, hence the crash in the showData method.

    You most probably don't need the map as a pointer.

    What I meant, is, store MyClass pointers instead of MyClass objects inside the Map:

    @
    QMap<QString, MyClass *> myObjs;
    @



  • Ah, yes! I can see it now. I've started down that path while waiting for a reply. Still hitting some seg faults, but I think I'm closer now to understanding this.

    At this point, I'm not even sure I'm doing the whole concept correctly. I basically need to have access to the values stored in the myObjs container class in various functions in the ObjsClass. They don't really have to be in a class (but I think I might need that to have them created in the heap in order to have access to the values.) I'll meddle some more to see if I can make heads/tails of all this.

    Java (and I'm not an expert there either) was little more forgiving, so I'm getting up to speed, just slowly.

    Thank you all for the help!!!



  • Ok. Now a call to ObjsClass::showData(&index) will trigger "MyClass" to show the parameters.

    Maybe this thread can help someone else. Here's what the code looks like that does what I want.

    header file didn't change.

    objclass.cpp:
    @

    #include "objsclass.h"

    ObjsClass::ObjsClass()
    {
    myObjs = new QMap<QString,MyClass*>;

    for (int i = 0; i < 8; i++) {
        MyClass *mc = new MyClass();
        mc->assignVals("paramA" + QString::number(i),"paramB" + QString::number(i),i);
        //myObjs["paramA" + QString::number(i)] = mc;
        myObjs->insert("paramA" + QString::number(i),mc);
    }
    

    }

    void ObjsClass::showData(QString &index) {
    qDebug() << "looking for:" << index;
    myObjs->value(index)->showData();
    }

    @

    Thanks again for all of the help!!!



  • Having it in a class as a container is a good idea. I do that in my project too. If you put them into a container as values (i.e. not as pointers), just make sure to have the implemented the assignment operators etc. It's not much work.

    Have a look at the following topcis of the "C++ FAQs":http://www.parashift.com/c++-faq-lite/

    And read on that FAQs in general - it's a pretty good source of valuable information!

    PS:
    If your ObjsClass object is long living (which I assume), it is ok to have the myObjs as a regular member, not as a pointer.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.