[Interest] QExplictlySharedDataPointer vs QSharedPointer

Thiago Macieira thiago.macieira at intel.com
Fri Sep 9 01:49:44 CEST 2016


On quinta-feira, 8 de setembro de 2016 21:08:37 PDT Elvis Stansvik wrote:
> > QESDP doesn't actually require that you derive from QSharedData,
> > only that you have an atomic integer called "ref" in your structure.
> 
> I see, that makes sense. And I see now that that's really all that
> QSharedData brings, the ref member.

Well, it does make the copy constructor private, so that you don't 
accidentally copy your shared data outside of the Q(E)SDP, thus accidentally 
copying the refcounter when it wasn't 1.

> >> In short, I fail to see a strong use case for QESDP compared to using
> >> a QSharedPointer. I'm probably missing something.
> > 
> > The fact that it's a smart pointer, not a raw one.
> 
> This I don't quite understand. Aren't they both smart pointers? They
> still seem quite similar to me. The difference mainly seems to be the
> slightly different API, and where the refcount is stored
> (external/internal).

Yes, but I wasn't comparing those two. I was comparing the use of a smart 
pointer to not using one. QESDP has a very limited use-case, as you've 
noticed. It's mostly used so you don't need to do the reference counting 
manually, like the older Qt classes do (QString, for example).

I also realise I was probably thinking of the even simpler QScopedPointer 
class.

> >> Does anyone know of a code base that makes liberal use of QESDP, or
> >> could you share how you're making use of it? It's easy to find uses
> >> for the implicit variant (QISDP).
> > 
> > qtxmlpatterns, I believe.
> 
> Thanks, I'll have a look at that and see how it's put to use there.

Not very well. In fact, it's an abuse of QESDP, so don't take it as a good 
example. I created QSharedPointer *because* qtxmlpatterns was abusing QESDP.

> With the above I was mainly looking for design advise: E.g. like when
> would I choose to use QESDP vs QSDP, and when I would use QSP instead
> of QESDP. I had also heard on IRC that some people preferred to always
> work with QESDP instead of QSDP, thinking the lack of control with
> QSDP was "dangerous", so was looking for opinions on that.

There are advantages for both QSDP and QESDP. It just depends on how often 
your code requires detaching. Take QDateTime as an example (before Qt 5.8). 
QSDP allows for easy detaching when necessary and allows you to write code as 
if you had a regular, raw pointer:

This doesn't detach:
bool QDateTime::isValid() const
{
    return (d->isValidDateTime());
}

This detaches:
void QDateTime::setDate(const QDate &date)
{
    d->setDateTime(date, time());
}

On the other hand, non-const non-simple functions could make the one serious 
mistake of QSDP. There's only one case in QDateTime but it's not obvious, so 
here's an example of the mistake from QTextCursor:

void QTextCursor::setKeepPositionOnInsert(bool b)
{
    if (d)
        d->keepPositionOnInsert = b;
}

The "if (d)" function makes a call to QSharedDataPointer::operator T*(), which 
detaches. The next line calls QSharedPointer::operator->(), which again tries 
to detach, even though we know (by construction) that the reference count is 1 
and there's no need to detach again. The compiler has no way of knowing that, 
so it generates code that will never be executed and a comparison whose result 
we already know.[1]

If we used QESDP, instead, we'd control exactly when to detach and, more 
importantly, when *not* to attempt to detach. On the other hand, you may make 
a mistake and forget to detach somewhere.

The solution we've found is to make QSDP detach explicitly and then never 
again. You can see this in QDateTime (before 5.8) and in QDir: many setter 
functions had code like

    QDateTimePrivate *d = this->d.data(); // detaches (and shadows d)

which causes a detach and then shadows the d variable so that further uses of 
d-> now use the raw pointer.

[1] This is also the most common problem with implicitly-detaching container 
classes in Qt. If you have a QString and do str[0] = 'a'; str[1] = 'b';, the 
code for detaching is present twice, even though we know by construction that 
the second one is not necessary.
-- 
Thiago Macieira - thiago.macieira (AT) intel.com
  Software Architect - Intel Open Source Technology Center




More information about the Interest mailing list