Analyzing a complete Qt project thoroughly



  • The project is illustrated in the official book of Qt under the name Spreadsheet, might you download it.

    My beginning question is:
    The class Cell inherits from the widget QTableWidgetItem publicly. One of its inherited methods is clone which is for creating a copy of the item:

    QTableWidgetItem* Cell::clone() const
    {
        return new Cell(*this);
    }
    

    Here Cell is called using an address (the address of the pointer pointing to the class Cell) as its argument, while the class Cell hasn't defined any parameters inside declaration!

    Second, what's the real job of clone here, in effect? If we remove that method, still we have cells drawn on the sheets.


  • Moderators

    This is calling a copy constructor. It has the following signature:

    Cell::Cell(const Cell&);
    

    You are right that the class does not define that explicitly, but the compiler generates this automatically if you don't specify it yourself. This is also true for a default constructor (without arguments), a move constructor and a destructor. Note that class Cell doesn't have a destructor ~Cell::Cell() defined either and yet the object is destroyed properly. That's because compiler generated that function for you.

    A copy constructor generated by the compiler performs a member-wise copy of the object i.e. if you have a class like

    class A{
       int foo;
       string bar;
    };
    

    The default generated copy constructor behaves as if you wrote:

    A::A(const A& other)
    {
        this->foo = other.foo;
        this->bar = other.bar;
    }
    

    so copies all members one by one.

    As for the purpose of clone() -it's a virtual function in the base QTableWidgetItem class. The purpose of it is to make a copy of an item. You can use it if you want to create a copy of an item. Qt also uses it internally for some stuff. This class Cell has some private members that we want to copy when a clone is created, so the function is overriden with a copy constructor.

    It's unfortunate that the example uses old style C++. If it used at least C++11 it would be more clear because that function would have the override keyword indicating that it overrides a virtual method of base class.

    If we remove that method, still we have cells drawn on the sheets.

    If you remove that function the base class method QTableWidgetItem::clone() will be used and it will call a copy constructor of QTableWidgetItem which won't copy the extra fields from class Cell. You would end up with a half-copied object. This example doesn't make a use of the cloning much, but overriding clone() is crucial if you want to have a correct behavior of a class that derives from QTableWidgetItem.


  • Qt Champions 2017

    @Chris-Kawa said in Analyzing a complete Qt project thoroughly:

    As for the purpose of clone() -it's a virtual function in the base QTableWidgetItem class.

    Indeed, this is the so-called "virtual copy constructor". As Chris explained, the idea is to be able to properly copy polymorphic types through their base pointers/references.


  • Banned

    This post is deleted!


  • Thanks to you both.

    As for C++11, will the function be modern (11) if we declare and define it this way:

    class Cell : public QTableWidgetItem
    {
    public:
        Cell();
    
        QTableWidgetItem* clone() const override;
    ...
    
    QTableWidgetItem* Cell::clone() const override
    {
        return new Cell(*this);
    }
    

    ?

    If so, it's just information for the reader/compiler and no change in anything else!

    The other question is, the overridden function clone is needed in practice for the reason you mentioned, but where/when is it called? (A virtual/overridden function must be called somewhere in the code to perform some task, naturally)
    It creates a copy (of what?), but in effect it will be called only when we put some data into the cell for the first time.

    If we draw out the view more, we have a spreadsheet class that inherits from QTableWidget and it's for our sheets. Then we use the class Cell as our cells on the sheets. So what is the word "copy" here!? (And I'm sure it's irrelevant to copying the content of a cell into another (ctrl+c)).


  • Qt Champions 2018

    @tomy said in Analyzing a complete Qt project thoroughly:

    It creates a copy (of what?)

    of "this" as you can see in the code. That means it creates a copy of the Cell instance on which it was called.
    Regarding the usage of the clone() method see https://doc.qt.io/qt-5/qtablewidget.html#setItemPrototype for example.


  • Qt Champions 2017

    @tomy said in Analyzing a complete Qt project thoroughly:

    If so, it's just information for the reader/compiler and no change in anything else!

    Correct. But this way the compiler provides checking as well, in case you accidentally overloaded the method instead of having an override.

    It creates a copy (of what?), but in effect it will be called only when we put some data into the cell for the first time.

    It creates a copy of the object. Having a base pointer you can't directly tell what kind of object is behind it, you only know that it's either of the type of the pointer or an instance of some derived class. So clone() gives you the ability to copy the actual polymorphic object. Consider the following:

    class A
    {
    public:
        virtual ~A();
        virtual A * clone() const;
    }
    
    class B : public A
    {
    public:
        A * clone() const override;
    };
    

    Say my library gives you a pointer to the base class - A *, can you tell if I created the object as an instance of A or as an instance of B? Assuming you want to copy the object, how can this be achieved if you don't know the actual type of the object behind the pointer? Here's where clone() makes sense, because in the method you know exactly the type of the object, that is to say:

    A * x = methodFromLibrary();
    A * copy = x->clone();
    

    Works properly because if you have an object of type A behind the pointer, then A::clone() is going to be called, conversely if the actual object is of type B then B::clone is going to be called. Meaning you're going to get the correct method called to make a copy. Remove the virtual out of A::clone and you can't copy the object anymore, because you'd always get A::clone() called.



  • @kshegunov

    Correct. But this way the compiler provides checking as well, in case you accidentally overloaded the method instead of having an override.

    Very good, thanks.
    Worth to know that when I write override as with the above post of me for the definition part, the compiler doesn't recognise it. Should I merely write it in the declaration?

    Say my library gives you a pointer to the base class - A *, can you tell if I created the object as an instance of A or as an instance of B?

    I suppose, no, because it's not yet been addressed.

    Assuming you want to copy the object, how can this be achieved if you don't know the actual type of the object behind the pointer? Here's where clone() makes sense, because in the method you know exactly the type of the object, that is to say:

    A * x = methodFromLibrary();
    A * copy = x->clone();
    

    Works properly because if you have an object of type A behind the pointer, then A::clone() is going to be called, conversely if the actual object is of type B then B::clone is going to be called. Meaning you're going to get the correct method called to make a copy. Remove the virtual out of A::clone and you can't copy the object anymore, because you'd always get A::clone() called.

    I know of virtual functions in C++, but the whole thing is rather mess in the use of this program.
    Thank you.

    I also set override for setData and data functions in Cell.


  • Qt Champions 2017

    @tomy said in Analyzing a complete Qt project thoroughly:

    Worth to know that when I write override as with the above post of me for the definition part, the compiler doesn't recognise it. Should I merely write it in the declaration?

    Yes, the override specifier goes only in the declaration, it's not part of the function prototype so it makes no sense in the definition.

    I suppose, no, because it's not yet been addressed.

    Incorrect. The object is there it's real, the handle to it (i.e. the pointer) is what hides the actual implementation. Notice the following is correct:

    A * x = new A;
    A * y = new B;
    

    Both these objects can be addressed through a pointer to the base class. Implicit upcasting is expected and normal. In the end B IS A.



  • @kshegunov

    How to see the definition of virtual QTableWidgetItem *clone() const; in qtablewidget.cpp on Qt Creator, please?

    In the Docs, it's only written it creates a copy of the item. I want firstly see the way it's defined in qtablewidget.cpp.


  • Qt Champions 2017

    Here. It's trivially defined as one'd expect.


  • Qt Champions 2018

    @tomy You have to install the Qt sources to see the cpp files - can be done with MaintenanceTool.

    Or have a look at https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qtablewidget.cpp.html



  • So starting from the main.cpp file, where all Qt and QML programs start executing all of their statements from, we begin by creating a new object of the project "MainWindow" using new on heap as below,

    #include <QApplication>
    #include "mainwindow.h"
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        
        MainWindow* mainWin = new MainWindow;
        mainWin->show();
        
        return app.exec();
    }
    

    In the constructor of MainWindow we have:

    MainWindow::MainWindow()
    {
        spreadsheet = new Spreadsheet;
        setCentralWidget(spreadsheet);
    
        createActions();
        createMenus();
        createContextMenu();
        createToolBars();
        createStatusBar();
    
        readSettings();
    
        findDialog = nullptr;
    
        setWindowIcon(QIcon(":/images/icon.png"));
        setCurrentFile("");
    }
    

    By spreadsheet = new Spreadsheet; the constructor of Spreadsheet is called, as shown below:

    Spreadsheet::Spreadsheet(QWidget *parent)
        : QTableWidget(parent)
    {
        autoRecalc = true;
    
        setItemPrototype(new Cell);
        setSelectionMode(ContiguousSelection);
    
        connect(this, SIGNAL(itemChanged(QTableWidgetItem *)),
                this, SLOT(somethingChanged()));
    
        clear();
    }
    

    then we specify the item prototype for the table to be new Cell. This, in turn, call's Cell's constructor:

    Cell::Cell()
    {
        setDirty();
    }
    

    Up to now, we have a spreadsheet table created on the main screen of the project's window and its cells' prototype is of a Cell, that is, they're by default dirty.

    All right up to here?

    Now, let me re-ask this question, please:
    where/when in code the overridden function clone is called?


  • Qt Champions 2018

    @tomy Put a break point inside your clone() and run through debugger. When debugger stops inside clone() you will have a stack trace where you can see from where it was called.


  • Lifetime Qt Champion

    Hi
    Unless you added new code to sample to call it, ( from to p post)
    Its not called as i can rename it to cloneX() and it still compiles meaning
    its not used at all. (currently)


  • Qt Champions 2017

    @tomy said in Analyzing a complete Qt project thoroughly:

    Now, let me re-ask this question, please:
    where/when in code the overridden function clone is called?

    Qt calls it when it needs a new object. That's why you call setItemPrototype. It (Qt) uses the object you passed to use it as item factory to create new items. It's unimportant for all practical purposes where exactly the call originates.



  • @jsulm

    I did it and it was the output. I think the stack trace part is the bottom-left window.
    1- What can be gained from this?

    0_1558599336654_Capture_1.PNG

    @mrjj, I didn't understand your sentences at all, sadly! -_-

    @kshegunov

    As I've said earlier, in effect clone will be called only when we put some data into a cell of the table for the first time.

    Qt calls it when it needs a new object. That's why you call setItemPrototype.

    2- Do you mean when we call setItemPrototype(new Cell); Qt calls clone? That is, whenever in the code we create a new cell using new that clone function is called.

    It (Qt) uses the object you passed to use it as item factory to create new items. It's unimportant for all practical purposes where exactly the call originates.

    I see, but for a learner of Qt, like me, it's good to boost their Qt abilities by finding answers even for small/detailed questions too.
    3- How do you know, when we create a new Cell, Qt calls that clone function? Where is it specified in the code, please?

    I numbered my questions so that you helpers don't ignore a question.
    4- Previously I asked, "All right up to here?". It's important for me, because I want to thoroughly understand the program's code, even step-by-step.

    Thanks to all.


  • Qt Champions 2018

    @tomy said in Analyzing a complete Qt project thoroughly:

    What can be gained from this?

    That clone was called from QTableModel::createItem, which was called from QTableModel::setData and so on.

    "2- Do you mean when we call setItemPrototype(new Cell); Qt calls clone? That is, whenever in the code we create a new cell using new that clone function is called." - please read https://doc.qt.io/qt-5/qstandarditemmodel.html#setItemPrototype It is explained there.

    3- How do you know, when we create a new Cell, Qt calls that clone function? Where is it specified in the code, please? - clone() is NOT called when you create an instance of Cell. Please read the link above. It is called whenever new item needs to be created:
    "Whenever QStandardItemModel needs to create an item on demand (for instance, when a view or item delegate calls setData()))"



  • @jsulm

    Thank you for your explanations, they're beneficial. But since the subject is somehow sophisticated and I myself am tackling various topics and these days very busy, I have some delay to come to this thread. sorry.

    I got much about clone() by now and will postpone further studying when I get to it in the code, when after the app is run completely.

    Back to studying the code via the step-by-step approach, I reached the statement setDirty(); in Cell.

    ==> After setDirty();, we step forward to itemChanged(QTableWidgetItem *) which is a signal to signify that the content of the item/cell is changed. Right? (1)

    Then we see the slot somethingChanged where recalculate() is called where all the cells in the spreadsheet there, will be set as dirty if they contain something. But why 'all' the cells? (2)

    The the viewport() function returns the viewport widget and the update() function updates (cleans) the rectangle inside the widget which is a cell here. Right? (3)

    The next question (4) is about emit modified();. What does it do or where is it used? I couldn't find somewhere it is used in code.


Log in to reply
 

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