PDA

View Full Version : Q_D: d-pointer private in this context?



fonzi337
23rd March 2012, 01:40
Hi,

I am trying to implement the D-pointer pattern for some of my classes. I've followed the tutorials http://zchydem.enume.net/2010/01/19/qt-howto-private-classes-and-d-pointers/ and http://techbase.kde.org/Policies/Library_Code_Policy/Shared_D-Pointer_Example but whenever I access a private member of my d-pointer, a compiler error results stating that the member is private in the given context. Q_DECLARE_PRIVATE is supposed to make my XYZPrivate class a friend of my XYZ class, so why is there a problem accessing its private data members?

Here's the relevant code:

Employer.h:


class EmployerPrivate;

class Employer : public Loggable
{
public:
Employer(QString name, QString market);
~Employer();

QList<Position> findJobs() const;
QString toString() const;
bool hire(Person& newHire, Position pos);

protected:
const QScopedPointer<EmployerPrivate> d_ptr;

private:
Q_DECLARE_PRIVATE(Employer)
};


Employer.cpp:


Employer::Employer(QString name, QString market) :
d_ptr(new EmployerPrivate(name, market))
{
}

Employer::~Employer()
{
}

QList<Position> Employer::findJobs() const
{
Q_D(const Employer);
return d->m_positions; // Compiler error: m_positions private within context
}

QString Employer::toString() const
{
Q_D(const Employer);
return QString("***Employer information***\nName: %1\nMarket: %2").arg(d->m_name).arg(d->m_market); // Compiler error
}


EmployerPrivate.h:


class EmployerPrivate
{
public:
EmployerPrivate(QString name, QString market);

private:
QList<Position> m_positions;
QString m_name;
QString m_market;
};


EmployerPrivate.cpp:


EmployerPrivate::EmployerPrivate(QString name, QString market) :
m_name(name),
m_market(market)
{
}


I've tried making both classes derive from QObject and added the Q_OBJECT macro just in case (even though, from my understanding, these aren't needed), but that also did not work.

ChrisW67
23rd March 2012, 06:31
Q_DECLARE_PRIVATE cannot possibly make Employer a friend of EmployerPrivate: to do that it would have to be inside the EmployerPrivate class. It does make EmployerPrivate a friend of Employer, i.e. the EmployerPrivate class can access the Employer private members.

Q_DECLARE_PUBLIC inside the private class has the effect you are seeking. However, you'll find the ClassPrivate class contents are public in the examples you linked to.

Lykurg
23rd March 2012, 06:34
Well the error message seems clear to me! m_positions is private. Solution: make it public. Thus you have to delete private: from the class EmployerPrivate. As most private classes only have public members.


EDIT: refresh before posting can help...

MarekR22
23rd March 2012, 12:11
Pattern looks like this:

// public Header file for SomeLibBaseClass
class SomeLibBaseClassPrivate;

class SomeLibBaseClass : public LibExternalClass {
...
protected:
SomeLibBaseClassPrivate *d_ptr; // or QScopedPointer<SomeLibBaseClassPrivate>
SomeLibBaseClass(SomeLibBaseClassPrivate &dd, other args); // needed for subclassing inside library

private:
Q_DISABLE_COPY(SomeLibBaseClass)
Q_DECLARE_PRIVATE_D(d_ptr, SomeLibBaseClass)
}

// -------------------------------------------------
// private header file for SomeLibBaseClass

class SomeLibBaseClass;

class SomeLibBaseClassPrivate {

Q_DECLARE_PUBLIC(SomeLibBaseClass)

public:
SomeLibBaseClassPrivate();

virtual ~SomeLibBaseClassPrivate(); // !!! OBLIGATORY virtual destructor. HAVE to do it if you want avoid strange hard to locate errors

// in general private class shouldn't have virtual methods (except destructor which is obligatory)
// It is never a Q_Object, it doesn't have meta data

SomeLibBaseClass *q_ptr;
}

// -------------------------------------------------
// public Header file for SomeLibSubClass

class SomeLibSubClassPrivate;

class SomeLibSubClass : public SomeLibBaseClass {

protected:
SomeLibSubClass(SomeLibSubClassPrivate &dd, otherArgs); // needed for subclassing inside lib

private:
Q_DECLARE_PRIVATE_D(d_ptr, SomeLibSubClass)
// no new d_pointer, it is inherited
};

// -------------------------------------------------
// private header file for SomeLibSubClass
#include "somelibbaseclass.h"
class SomeLibSubClass;

class SomeLibSubClassPrivate : public SomeLibBaseClassPrivate {

Q_DECLARE_PUBLIC(SomeLibSubClass)

public:
SomeLibSubClassPrivate();

~SomeLibBaseClassPrivate(); // here is optional

// q pointer is not needed, it is inharited
}

macro Q_DECLARE_PRIVATE_D and Q_DECLARE_PUBLIC (you didn't use that macro that is why you have a problem, don't forget about q_pointer) provides friendship between public and private class. You should use both.

It would be the best if you take a look how Qt is written. There you have all cases of d_pointer usage.
For example objects with implicit data sharing (like QString, QByteArray, QVector, QList) have a different set of roles.

fonzi337
23rd March 2012, 17:09
Thank you MarekR22, that works great. I'm wondering though: is q_pointer required? I thought that was only necessary if the private class needed to access the private members of the public class. Do I still need to include q_pointer if my private class does not need to access the public class' private members (since I get a compiler error when I take out the q_pointer, it seems like it does need it; I'd just like to understand better why it is needed)?

Thanks!

ChrisW67
23rd March 2012, 20:49
The public class will generally have no private members other than the d pointer.

The q pointer is primarily used to access the signals of the publicly visible class, which are declared protected in the public class, from the private impl class. This is a convenience.. otherwise the impl class would have to emit signals, that were connected to trigger the public class signal of the same function.

fonzi337
27th March 2012, 21:29
Thanks for the explanation Chris, that makes a lot of sense.