Ronald Ku
Ronald Ku

Reputation: 327

D-pointer/pimpl pattern base class pointer access derived class member

I have misunderstanding in the concept in this link. http://wiki.qt.io/D-Pointer


In the section Inheriting d-pointers for optimization, when creating a Label object, say

Label B;

The base constructor will be called first and then the derived constructor will be called. Since the derived constructor is in this form:

Label::Label()
: Widget(*new LabelPrivate) // initialize the d-pointer with our own 
Private
{
}

This base constructor is called.

Widget::Widget(WidgetPrivate &d) : d_ptr(&d)
{
}

The base class member

WidgetPrivate *d_ptr

will have the value of

*new LabelPrivate

I know that the object B can access the protected member d_ptr which is in a type of WidgetPrivate *. My problem is, can one use this d_ptr to access the text member in LabelPrivate?

If yes, my concern is that why d_ptr in the type of WidgetPrivate* can have access to the derived class member text?

If no, does this mean Label has no access to the member text in LabelPrivate and then what is the point of using the pattern?


To avoid ambiguity, I post the original material here: widget.h

class Widget
{
public:
    Widget();
    // ...
protected:
    // only subclasses may access the below
    // allow subclasses to initialize with their own concrete Private
    Widget(WidgetPrivate &d);
    WidgetPrivate *d_ptr;
};

widget_p.h

struct WidgetPrivate
{
    WidgetPrivate(Widget *q) : q_ptr(q) { } // constructor that initializes 
the q-ptr
    Widget *q_ptr; // q-ptr that points to the API class
    Rect geometry;
    String stylesheet;
};

widget.cpp

Widget::Widget() : d_ptr(new WidgetPrivate(this))
{
}

Widget::Widget(WidgetPrivate &d) : d_ptr(&d)
{
}

label.h

class Label : public Widget
{
public:
    Label();
    // ...
protected:
    Label(LabelPrivate &d); // allow Label subclasses to pass on their         Private
    // notice how Label does not have a d_ptr! It just uses Widget's d_ptr.
};

label.cpp

#include "widget_p.h"

class LabelPrivate : public WidgetPrivate
{
public:
    String text;
};

Label::Label()
 : Widget(*new LabelPrivate) // initialize the d-pointer with our own Private
{
}

Label::Label(LabelPrivate &d) : Widget(d)
{
}

Upvotes: 0

Views: 601

Answers (1)

Peter
Peter

Reputation: 14947

To a Widget, d_ptr is only a WidgetPrivate*. A Label would have to cast it back to a LabelPrivate* to access its contents.

If you read one section further down on the page you cite, you'll see the full code for actually getting to the contents of LabelPrivate:

void Label::setText(const String &text)
{
    LabelPrivate *d = static_cast<LabelPrivate*>(d_ptr); // cast to our private type
    d->text = text;
}

Then the document goes on to describe some Qt macros that hide this verbosity, but the implementation is still the same.

To summarize the implications of the pattern: Both the top-level class and the PIMPL pointer are hierarchies. A subclass of Widget (Label) sets the PIMPL pointer to a subclass of WidgetPrivate (LabelPrivate). Now a Widget implementation can access any part of the WidgetPrivate superclass, because that's the covariant type of the stored pointer (a subclass pointer is always castable to its superclasses). However, it knows nothing about the subclass LabelPrivate.

Once you're inside a Label implementation, you can recast the PIMPL pointer back to the subclass version (LabelPrivate) that you know is in there, and have full access to the contents.

Upvotes: 1

Related Questions