Best way to initialize QList?



  • Hello there!

    I have been trying to initialize a QList to use in my program, but have been having trouble. The QList needs to be a member of the class NBhistoryInterface. QList.prepend() is called by one of my slots, and this requires that the QList be initialized. If I don't initialize it just above the .prepend() call, the program will compile and then crash. I would like to be able to initialize it in the constructor. Here is my code:

    .h file:
    @
    public:
    QList<QString> m_historyTitle;
    @

    constructor, If I initialize it this way the program crashes:
    @
    NBhistoryInterface::NBhistoryInterface(QObject *parent)
    : QWebHistoryInterface(parent)
    {
    QList<QString> m_historyTitle;
    }
    @

    addHistoryItem Slot, this works:
    @
    void NBhistoryInterface::addHistoryItem(QString title, QString url)
    {
    qDebug() << "SLOT: NBhistoryInterface::addHistoryItem(const NBhistoryItem *input) STATUS: Called";
    QList<QString> m_historyTitle;
    m_historyTitle.prepend(title);
    qDebug() << "SLOT: NBhistoryInterface::addHistoryItem(const NBhistoryItem *input) STATUS: Completed";
    }
    @

    The problem with initializing it in the addHistoryItem slot is that the QList will be initialized every time this slot is called, I need the QList to be initialized once. How do I do this? I would like to do it in the constructor.

    Thanks!



  • Hi, in c++ if you have a member on the stack memory like you have
    @
    public:
    QList<QString> m_historyTitle;
    @
    it will be automatically initialized when you create an object of your class, it will call the constructor of QList<QString> and initialize it, so you don't have to do anything usually.
    When you use a pointer you have to initialize it yourself in your constructor or somewhere else before you can use it, but in your example that is not necessary, so if you program crashes that is not the problem of your QList I would say!?

    But you should remove the expression "QList<QString> m_historyTitle;" in all your functions, why are you redefining your list over and over again? you only need to do that once in your header, and not in the constructor or your slot, that will hide the member and use a local variable instead (I think), better don't use the same variable names for local variables and class members in the first place. :)

    Just a tip: learn the pure c++ basics first before using Qt, seems like you are not using const references as parameters in your slot, that is like the default way in your case I would say:
    @
    const QString &param
    // instead of just
    QString param
    @



  • ok then, I changed the .cpp code to this per your recommendation:
    @
    NBhistoryInterface::NBhistoryInterface(QObject *parent)
    : QWebHistoryInterface(parent)
    {
    //QList<QString> m_historyTitle;
    }

    void NBhistoryInterface::addHistoryEntry(QString title, QUrl url)
    {
    qDebug() << "SLOT: NBhistoryInterface::addHistoryEntry(const QUrl &url) STATUS: Called";
    QString Url = url.toString();
    addHistoryItem(title, Url);
    qDebug() << "SLOT: NBhistoryInterface::addHistoryEntry(const QUrl &url) STATUS: Completed";
    }

    void NBhistoryInterface::addHistoryItem(QString title, QString url)
    {
    qDebug() << "SLOT: NBhistoryInterface::addHistoryItem(const NBhistoryItem *input) STATUS: Called";
    //QList<QString> m_historyTitle;
    m_historyTitle.prepend(title);
    qDebug() << "SLOT: NBhistoryInterface::addHistoryItem(const NBhistoryItem *input) STATUS: Completed";
    }
    @
    the .h code has not changed.

    I am still getting a crash with the following error report:

    0 <BUNDLEIDENTIFIER> 0x00000001000178d4 QList<QString>::prepend(QString const&) + 20 (qlist.h:546)
    1 <BUNDLEIDENTIFIER> 0x0000000100016cde NBhistoryInterface::addHistoryItem(QString, QString) + 158 (nbhistory.cpp:

    I'm not really sure what its trying to say. Ideas?



  • Sorry I changed my last post a little, but still with your modifications I don't see any errors in that code, it has to be somewhere else but I believe some guy had a similar problem here on the forums, are you running that on iOS or Mac OS X?
    You should run the app in the debugger, then you will see why it will crash and in what function exactly, hard to say like this where the problem is coming from.



  • yeah is OS X 10.9 Mavericks. Could it have something to do with the string I'm passing to .prepend()?



  • I don't think so, it would be pretty hard to pass an invalid QString to that parameter:
    but you can test the list somewhere else maybe, just in the constructor or something prepend some static string and see if that works for you.



  • The docs say it needs a const QString &string. Is that what I'm passing? And how do I run it in the debugger?



  • you do give it a const string in the end, but as I've said in my first post you should use const references as your parameter or the compiler will copy the strings (which is unnecessary, that is c++ :D ):
    @
    addHistoryItem(QString title, QString url)
    // to
    addHistoryItem(const QString &title, const QString &url)
    @
    but that has nothing to do with the error, it's just better and faster c++ code in this case I would say.

    Again do a small code example and only test the list, maybe in a standalone app to see how it works and get familiar with Qt, I don't know.

    You an start the app in debug mode if you click on the debug action instead of run from Qt Creator (F5 on windows, don't know on mac)!?



  • Ok debugger is running. It took me to qlist.h and drew a yellow arrow next to the following code:
    @
    template <typename T>
    inline void QList<T>::prepend(const T &t)
    {
    if (d->ref.isShared()) { //<-- THIS LINE (line 546)
    Node *n = detach_helper_grow(0, 1);
    QT_TRY {
    node_construct(n, t);
    } QT_CATCH(...) {
    ++d->begin;
    QT_RETHROW;
    }
    @
    Its line 546. This code ain't like no C++ I've ever seen, so I have no clue what its trying to do or why theres an arrow there.
    What do you think?



  • I don't think you need to worry about what the Qt classes internally do, but if you wanna know: in that case it checks if the QList's data is shared and if so it will detach itself and do a deep copy on the fly. Most Qt classes use implicit data sharing, they will only do a shallow copy if you copy the objects and the deep copy is only triggered if you actually change the object (like what you do with prepend you are going to change the list so it detaches itself), that is also called "copy on write".
    here a simple example with a QString
    @
    QString a = "foo";
    QString b(a); // will call the copy constructor of QString, usually doing a shallow copy for most Qt classes (will only copy a pointer to the data, not the data itself)
    a == b; // true
    b.append("bar"); // you modify the QString and this will internally detach the data and do a deep copy
    // if you just run read only operations on the string it will never copy the data
    @
    That is just the background info, but I don't think there is a bug in the QList code, so why is your app crashing there? weird.

    So back to your problem, hard to find in this case, can you show the full header file or even full code, I don't know how much you have in that class.



  • Ok here comes all the code (Its a lot to sift through)

    NBhistory.h:
    @
    #ifndef NBHISTORY_H
    #define NBHISTORY_H

    #include <QObject>
    #include <QWebHistoryInterface>
    #include <QDateTime>
    #include <QUrl>
    #include <QString>
    #include <QMenu>
    #include <QDebug>

    class NBhistoryInterface : public QWebHistoryInterface
    {
    Q_OBJECT
    public:
    explicit NBhistoryInterface(QObject *parent = 0);
    void addHistoryEntry(QString Title, QUrl url);
    void addHistoryItem(QString title, QString url);
    void saveHistory();
    void clearHistory();
    QString historyTitle(int x);
    QString historyUrl(int x);

    QList<QString> m_historyTitle;
    QList<QString> m_historyUrl;
    

    virtual
    bool historyContains(const QString &url) const = 0;

    signals:

    public slots:

    private:

    };

    class NBhistoryAction : public QAction
    {
    Q_OBJECT
    public:
    explicit NBhistoryAction(QWidget *parent = 0);

    private:
    };

    class NBhistoryMenu : public QMenu
    {
    Q_OBJECT
    public:
    explicit NBhistoryMenu(QWidget *parent = 0, NBhistoryInterface *interface = 0);
    void populate();

    private:
    NBhistoryInterface *m_history;
    NBhistoryAction *action1;
    NBhistoryAction *action2;
    NBhistoryAction *action3;
    NBhistoryAction *action4;
    NBhistoryAction *action5;
    NBhistoryAction *action6;
    NBhistoryAction *action7;
    NBhistoryAction *action8;
    NBhistoryAction *action9;
    NBhistoryAction *action10;
    };

    #endif // NBHISTORY_H
    @

    NBhistory.cpp:
    @
    #include "nbhistory.h"

    NBhistoryInterface::NBhistoryInterface(QObject *parent)
    : QWebHistoryInterface(parent)
    {
    //QList<QString> m_historyTitle;
    }

    void NBhistoryInterface::addHistoryEntry(QString Title, QUrl url)
    {
    qDebug() << "SLOT: NBhistoryInterface::addHistoryEntry(const QUrl &url) STATUS: Called";
    QString Url = url.toString();
    addHistoryItem(Title, Url);
    qDebug() << "SLOT: NBhistoryInterface::addHistoryEntry(const QUrl &url) STATUS: Completed";
    }

    void NBhistoryInterface::addHistoryItem(QString title, QString url)
    {
    qDebug() << "SLOT: NBhistoryInterface::addHistoryItem(QString title, QString url) STATUS: Called";
    //QList<QString> m_historyTitle;
    m_historyTitle.prepend(title);
    //m_historyTitle.insert(0, title);
    qDebug() << "SLOT: NBhistoryInterface::addHistoryItem(QString title, QString url) STATUS: Completed";
    }

    void NBhistoryInterface::saveHistory()
    {

    }

    void NBhistoryInterface::clearHistory()
    {
    m_historyTitle.clear();
    m_historyUrl.clear();
    }

    QString NBhistoryInterface::historyTitle(int x)
    {
    return m_historyTitle.at(x);
    }

    QString NBhistoryInterface::historyUrl(int x)
    {
    return m_historyUrl.at(x);
    }

    NBhistoryAction::NBhistoryAction(QWidget *parent)
    : QAction(parent)
    {

    }

    NBhistoryMenu::NBhistoryMenu(QWidget *parent, NBhistoryInterface *interface)
    : QMenu(parent)
    {
    m_history = interface;
    }

    void NBhistoryMenu::populate()
    {
    qDebug() << "SLOT: NBhistoryMenu::populate() STATUS: Called";
    this->removeAction(action1);
    this->removeAction(action2);
    this->removeAction(action3);
    this->removeAction(action4);
    this->removeAction(action5);
    this->removeAction(action6);
    this->removeAction(action7);
    this->removeAction(action8);
    this->removeAction(action9);
    this->removeAction(action10);

    action1 = new NBhistoryAction(this);
    qDebug() << "Ln 81 Called";
    //QString text = m_history->historyTitle(0);
    qDebug() << "Ln 81 Complete";
    //action1->setText(text);
    addAction(action1);
    
    action2 = new NBhistoryAction(this);
    //action2->setHistoryItem(m_history->history(1));
    addAction(action2);
    
    action3 = new NBhistoryAction(this);
    //action3->setHistoryItem(m_history->history(2));
    addAction(action3);
    
    action4 = new NBhistoryAction(this);
    //action4->setHistoryItem(m_history->history(3));
    addAction(action4);
    
    action5 = new NBhistoryAction(this);
    //action5->setHistoryItem(m_history->history(4));
    addAction(action5);
    
    action6 = new NBhistoryAction(this);
    //action6->setHistoryItem(m_history->history(5));
    addAction(action6);
    
    action7 = new NBhistoryAction(this);
    //action7->setHistoryItem(m_history->history(6));
    addAction(action7);
    
    action8 = new NBhistoryAction(this);
    //action8->setHistoryItem(m_history->history(7));
    addAction(action8);
    
    action9 = new NBhistoryAction(this);
    //action9->setHistoryItem(m_history->history(8));
    addAction(action9);
    
    action10 = new NBhistoryAction(this);
    //action10->setHistoryItem(m_history->history(9));
    addAction(action10);
    qDebug() << "SLOT: NBhistoryMenu::populate() STATUS: Completed";
    

    }
    @



  • The application output:
    @
    QWidget::setLayout: Attempting to set QLayout "" on mainView "", which already has a layout
    QWidget::setLayout: Attempting to set QLayout "" on QWmessageBox "", which already has a layout
    QWidget::setLayout: Attempting to set QLayout "" on QWmessageBox "", which already has a layout
    QWidget::setLayout: Attempting to set QLayout "" on QWwindow "", which already has a layout
    SLOT: NBhistoryInterface::addHistoryEntry(const QUrl &url) STATUS: Called
    SLOT: NBhistoryInterface::addHistoryItem(QString title, QString url) STATUS: Called
    The program has unexpectedly finished.
    @

    ...And part of the Apple crash report:
    @
    Exception Type: EXC_BAD_ACCESS (SIGSEGV)
    Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000010

    VM Regions Near 0x10:
    -->
    __TEXT 0000000100000000-000000010002e000 [ 184K] r-x/rwx SM=COW /Users/USER/Desktop/*/NovaBrowser_v2.app/Contents/MacOS/NovaBrowser_v2

    Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
    0 <BUNDLEIDENTIFIER> 0x00000001000178d4 QList<QString>::prepend(QString const&) + 20 (qlist.h:546)
    1 <BUNDLEIDENTIFIER> 0x0000000100016cde NBhistoryInterface::addHistoryItem(QString, QString) + 158 (nbhistory.cpp:21)
    @

    I am certainly no expert but the following lines of the error report:
    @
    Exception Type: EXC_BAD_ACCESS (SIGSEGV)
    Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000010
    @
    look like there is some issue with a memory address. Is this the case?



  • Ok I added your whole class to y new widget app, I don't use webkit much so I just used the class as is (without webkit) and created an object of it myself.

    First problem: I don't know if that is like it should be but seems your class misses an abstract method from its base class and also an implementation for your still abstract method:
    @
    bool historyContains(const QString &url) const = 0; // abstract method
    void addHistoryEntry(const QString &url); // pure virtual method from base, class, not present in your NBhistoryInterface
    @
    without those 2 addition I could not create an object of your class of course.. just wondering if that intended with webkit, seems like an error to me?

    After I fixed that I could compile and use your class like this in my example:
    @
    NBhistoryInterface hi;
    hi.addHistoryEntry(QStringLiteral("test"), QUrl("http://www.test.com"));
    @
    no errors or crash, output was:
    @
    SLOT: NBhistoryInterface::addHistoryEntry(const QUrl &url) STATUS: Called
    SLOT: NBhistoryInterface::addHistoryItem(QString title, QString url) STATUS: Called
    SLOT: NBhistoryInterface::addHistoryItem(QString title, QString url) STATUS: Completed
    SLOT: NBhistoryInterface::addHistoryEntry(const QUrl &url) STATUS: Completed
    @

    Edit. from your post I don't know how you can compile your class but it seems the framework is trying to call
    @
    NBhistoryInterface::addHistoryEntry(const QUrl &url)
    @
    which you don't have in your class (see my error report above), so that should be an compiler error and no runtime crash at all!?



  • Yeah Im not sure why my class is allowed to compile. How exactly did you fix the class? Could you post your improved class code please? I am not sure why the compiler doesn't error out when it tries to call addHistoryEntry(const QUrl &url). Possibly a glitch in the compiler?



  • Lol glitch in the compiler, next time it is a glitch in the matrix... Just kidding
    More likely because you never create an object of your class yourself, that is why you won't get a compiler error. But the WebKit or wherever your class is being used has to create an object somehow and I guess there is where your app might crash, maybe.
    Best way to fix the methods, just do as I did in my test and create an object of your NBhistoryInterface and compile, you will then see what I mean, hopefully.



  • fix the methods how? Should I overload my addHistoryEntry() with the Qt default one? Im not sure whats wrong with historyContains().



  • Sorry I assumed you have some basic c++ or general object oriented programming knowledge and know what to do!?

    I can't give you a complete tutorial on how do program c++, but this is the short version for your problem: if you implement an interface or abstract class you have to implement all pure virtual functions (functions without any body, also called abstract methods), so you can create objects of that class.
    The Qt documentation usually tells you what those methods are or you let the compiler tell you if you create an object of the class it will give you the error(s).
    You want to implement the interface QWebHistoryInterface, if you look at the "doc":http://qt-project.org/doc/qt-5/qwebhistoryinterface.html you'll see:

    bq. It contains two pure virtual methods that are called by the WebKit engine: addHistoryEntry() is used to add urls that have been visited to the interface, while historyContains() is used to query whether the given url has been visited by the user.

    so you need to override and implement at least those two functions with this exact parameters (you have a function addHistoryEntry, but the parameters don't match so it's not an override of the base function)
    @
    virtual void addHistoryEntry(const QString & url) = 0
    virtual bool historyContains(const QString & url) const = 0
    @
    In your code you are missing those 2 methods, if I am not mistaken.

    If you use a c++11 compiler you can make your life a little bit easier by using the new keyword "override", to be sure you are overriding the methods of the base class and not creating new ones by accident (like you did), example:
    @
    void addHistoryEntry(const QString & url) override {
    // your code
    }
    @
    see that "override" keyword after the method definition? if the base class does not contain a method with that exact definition you will get a compiler error, without the need to create an object of your class.

    Hope that helps.



  • Sorry for the late response, Ive been busy with AP testing :/

    I do have some basic knowledge of c++, but I have been getting pretty confused on some of the basics such as virtual functions, etc. I thought that if I hoped right into Qt and c++ I would learn better then just reading tutorials, guess I was kinda wrong haha :) I have started watching/reading some tutorials on the internet, and I've ordered several books from the library. They should be coming in soon!

    Ok back to the question at hand. I just added
    @
    void addHistoryEntry(const QString & url) = 0
    bool historyContains(const QString & url) const = 0
    @
    to the 'virtual' section of my .h file. The program still crashes after compilation and I'm a little confused: you said virtual functions were functions without bodies, but later on you talked about putting the 'override' keyword in what I'm assuming is the method implementation in the .cpp file (forgive me if I misunderstood). I'm assuming 'override' tells the compiler to skip over this definition and use another one (like the one I created)? Again I'm probably wrong. I am using the compiler built into Qt Creator, does that support 'override'?

    Thanks for all your help! I am really interested in programming and c++, but as a high schooler I don't have much time to study it and there aren't any computer classes at my school so I've been teaching myself. I know I'm probably asking some pretty amateur questions, but I am really trying my best to learn.
    Thanks for your help!



  • While reading the QList docs again I found this:

    "QList does not support inserting, prepending, appending or replacing with references to its own values. Doing so will cause your application to abort with an error message."

    I'm not sure what it means by "references to its own values", but I'm assuming that means references to values already in the QList. Is that correct?



  • Xander84,

    I have changed up the NBhistory.cpp file. Whenever I try and run it, it generates an error "expected function body after function declarator"
    Im not sure what that means. My most recent code is on GitHub. Heres the link (its the NBhistory.cpp/.h file):
    "Your text to link here...":https://github.com/NovaBrowser/NovaBrowser



  • Hey (this is Xander84, my other account is corrupted or something I can't use it atm, that's why I wasn't answering sooner, sorry).

    1. "I’m assuming ‘override’ tells the compiler to skip over this definition and use another one (like the one I created)?"
      override tells the compiler that it has to keep the "contract" with the method of the base class. So I'would say it is exactly the opposite of what you said. :D
      the override keyword is optional and does nothing really, all it is used for is helping the developer overriding the correct method and not (what you said) creating new ones by accident. If you have ever done some Java there is a @Override annotation with the same effect:
      @
      @Override void foo() { } // java
      // similar to c++11 override keyword
      void foo() override { } // c++11
      @

    2. the virtual keyword "just" indicates that a method can be overridden by child classes, without declaring a method "virtual" you cannot override it in c++ (unlike other programming langues, e.g. Java is everything virtual unless you define it "final")

    3. about your QList question, in believe in Qt you cannot save references in any of the container classes. e.g.
      @
      QList<QString&> invalidList;
      @
      that is meant by list of references, since you can't do that I don't know how you would add a reference to its own member if you only use plain objects (copies) or pointers...

    4. on your GitHub page you are still missing the implementation of historyContains in your nbhistory.cpp file? I see the definition in the header only, that is a syntax error in c++. Maybe not that obvious to you why? your are defining a non-virtual abstract method, which is impossible to implement by any child classes, so it's a syntax error. The virtual keyword you used is only for the next method:
      @
      virtual
      void addHistoryEntry(const QString & url) = 0;
      bool historyContains(const QString &url) const = 0;
      // format as
      virtual void addHistoryEntry(const QString & url) = 0;
      bool historyContains(const QString &url) const = 0;
      @
      virtual is not like the visibility modifier (public, private, etc), there is no following ":".
      anyway you don't need a a virtual there, unless you want to override the method in a child class later (I guess you don't have any), so just remove it. You should know what virtual means by now since I briefly explained it above, if not just search for it on the net.
      Also "= 0" after a method definition means the method is abstract (has no implementation body at all), so you should remove that too, you cannot have abstract methods if you want to create objects of that class.
      That said, just do it like this:
      @
      void addHistoryEntry(const QString & url) override;
      bool historyContains(const QString &url) const override;
      @
      and provide implementations for both methods in your cpp file. :)
      if you don't have c++11 remove the override keyword there, tip to compile your project with c++11 support you onyl need to tell Qt to use it, most modern compilers should be able to support it. in your qmake project file add:
      @
      CONFIG += c++11
      @
      that only tells qmake to use the compiler flags (not necessary for all compilers), but with this config it works on all supported compiler by Qt.

    (sorry for the long text again)



  • Ok so I have since converted NBhistoryManager into a subclass of QObject. This way I don't have to deal with overridden functions and all that jazz. I am still having trouble with the program crashing when it calls QList.prepend(). The implementation of the m_historyTitle has not been changed.


Log in to reply
 

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