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 classCell
inherits from the widgetQTableWidgetItem
publicly. One of its inherited methods isclone
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 classCell
) as its argument, while the classCell
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. -
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 baseQTableWidgetItem
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 classCell
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 ofQTableWidgetItem
which won't copy the extra fields from classCell
. You would end up with a half-copied object. This example doesn't make a use of the cloning much, but overridingclone()
is crucial if you want to have a correct behavior of a class that derives fromQTableWidgetItem
. -
@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.
-
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 fromQTableWidget
and it's for our sheets. Then we use the classCell
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)). -
@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. -
@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 ofA
or as an instance ofB
? 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 whereclone()
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, thenA::clone()
is going to be called, conversely if the actual object is of typeB
thenB::clone
is going to be called. Meaning you're going to get the correct method called to make a copy. Remove thevirtual
out ofA::clone
and you can't copy the object anymore, because you'd always getA::clone()
called. -
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 writeoverride
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 ofA
or as an instance ofB
?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, thenA::clone()
is going to be called, conversely if the actual object is of typeB
thenB::clone
is going to be called. Meaning you're going to get the correct method called to make a copy. Remove thevirtual
out ofA::clone
and you can't copy the object anymore, because you'd always getA::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
anddata
functions inCell
. -
@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
ISA
. -
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.
-
@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 ofSpreadsheet
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 functionclone
is called? -
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) -
@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. -
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?@mrjj, I didn't understand your sentences at all, sadly! -_-
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 callsclone
? That is, whenever in the code we create a new cell usingnew
thatclone
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 anew Cell
, Qt calls thatclone
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.
-
@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()))" -
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();
inCell
.==> After
setDirty();
, we step forward toitemChanged(QTableWidgetItem *)
which is a signal to signify that the content of the item/cell is changed. Right? (1)Then we see the slot
somethingChanged
whererecalculate()
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 theupdate()
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.