[Qt-interest] QList, copy problems

Till Oliver Knoll till.oliver.knoll at gmail.com
Sat Oct 15 19:32:44 CEST 2011


Am 15.10.11 16:17, schrieb Thiago Macieira:
>> ... if you want to have two completely separate
>> "instances" of your list which you can modify without affecting the
>> other instance, then you need a deep copy.
> 
> Not really. QList will make a deep copy the moment that you try to modify it. 
> So you don't need to make a deep copy before QList makes a deep copy for you.

Just for clarity: a "deep copy" is an operation where every element and
sub-element thereof is copied, hence you end up with two distinct
instances having the same structure.

In particular when you "deep copy" a list then you end up with two
instanes of a list, each pointing to /different/ items (which have been
"deep copied" before).

So no, QList does /not/ make deep copies.

What you refer to is "copy on write". QList is value-based, and the
value the OP is refering to is a "pointer to something".

So you are right that initially the copied QList instance will share the
same "list data" (much like a copied QString refers to the same data
initially), and only when the copy (or the original) is modified is the
copy operation actually executed - "copy on write".

And what is copied? The value - in this case the pointer.

However the thing the pointer points at is /not/ copied.

> 
>> If on the other hand you want the items in the list to be unique, then
>> you make a shallow copy, but you have to be aware that when you delete
>> those instances your other copy of the list becomes invalid.
> 
> That has nothing to do with QList and copying. If you want unique elements, 
> you can't copy the list, you need to create a new one and create new items.

Well, my formulation was unfortunate: with "unique" I meant that the
objects are /not/ copied (such that they stay "unique"). Anyway, a
"shallow copy" gives you two lists with copied pointers, but the copied
pointer still points to the /same/ object. And if this is what you want,
well then, just take care about deallocation, which invalidates the
pointer in the other list!

So if you want to avoid this, then you have exactly to do what Thiago
mentioned: create a second QList instance, copy each object (not just
the pointer!) with a "deep copy" (so if those instances point to other
data, also "deep copy" that data etc.) and insert each copied object
from list A into list B.

And there you go: "deep copy" ;)

>> And according to the description of the OP it just appeared to me that
>> this is what happens ...
> 
> Sounds to me like a bad question. 

Agreed. It wasn't clear what the OP actually wants to achieve.

> If you do this:
> 
> 	QList<QObject *> list;
> 	list << new QObject;
> 
> 	// copy the list:
> 	QList<QObject *> list2 = list;
> 
> 	// "modify the list"
> 	delete list[0];
> 
> 	// access the copy
> 	qDebug() << list2[0];
> 
> The above might crash. But it's not because of deep or shallow copies. It's 
> also not because the original list was modified

When I said "the list being modified" then I actually meant in this
context that the actual item was modified/deleted. But yes, technically
you're right, the list itself wasn't modified. And that leads then
exactly to the problem that the pointer still points to the now invalid
address.

Anyway, what you describe is exactly the scenario I meant.

And now depending on your needs you have two choices:

a) Be aware that when you delete the object that the other list (or
rather: one of its items) becomes invalid (if a shallow copy is what you
really want)

or

b) Make a deep copy to avoid above scenario (but then possibly wasting
memory and deep-copy might be expensive)

>  -- it wasn't! The problem was 
> that the object pointed to by *both* lists was deleted.
> 
> If we change the copying of the list by a deep copy:
> 
> 	QList<Object *> list2;
> 	list2 << list.at(0);
> 
> The problem is still present.

Except that the above is /not/ a deep copy: You did not deep copy the
actual item!

This is how you would do it (pseudo code):

  QList<QObject *> origList;
  QList<QObject *> copyList;

  for each(QObject *object, origList) {
    // Assumes that copy() creates a proper "deep copy" of itself
    QObject *objectCopy = object->copy();
    copyList.append(objectCopy);
  }

or maybe it would even be possible to overwrite the = assignment
operator or whathever. Also I am aware that QObject probably doesn't
have a copy() method, but I left any casting etc. away. You get the idea
anyway...

> 
>> Hence my formulation "you *might* need a deep copy", as we simply have
>> not enough information about the particular use-case. And we don't know
>> how and when the list items are deallocated.
> 
> And my formulation of "deep copies are never necessary" is also correct. It's 
> not about the list's contents, but objects pointed to.

Uhm well, actually I still kind of disagree with "deep copies are never
necessary", but it might be because we have a different understanding of
"deep copy" ;) But I think we mean the same thing above anyway ;)

Cheers, Oliver




More information about the Qt-interest-old mailing list