Qt: Emit signal when QPointer is changed

562 Views Asked by At

I want to make a class similar to QPointer (or subclass it) which will emit a signal whenever the internal pointer gets changed.

Unfortunately I need to inherit QObject class to be able to emit signals and also I think I need to use templates to be able to store a specific type of pointer. But according to this question it is not possible to mix QObject with templates.

Any idea how to make my class which keeps track on internal pointer changes?

1

There are 1 best solutions below

3
On BEST ANSWER

You should use QPointer when you want to guard a QObject subclass instance. If the referenced pointer is deleted, QPointer is set to 0:

QLabel * label = new QLabel();
QPointer pointer = label;
delete label;
if(pointer) //false
{
//...

Since the held object is a QObject subclass, you can connect a slot to its destroyed signal, to track when it's deleted (and the QPointer is set to zero).

If a QPointer got reassigned, instead, no destroyed signal will be emitted

pointer = new QLabel();

the formerly held object is only left unguarded, but not deleted.

To track the pointer reset, you'd be better using QSharedPointer, instead. When the QSharedPointer held object is cleared or reset, and the internal reference count gets to zero, delete is called on the held object (and if it's a QObject subclass, the destroyed signal is emitted).

If you go for a DIY smart pointer, I suggest to use a QObject derived class for emitting signals and own an instance of it in your pointer class:

class Emitter : public QObject
{
    Q_OBJECT
public:
    Emitter() : QObject(0) {}
    void forward(void * p) { emit reset(p); }
signals:
    void reset(void *);
};

template <typename T>
class Pointer
{
public:
    Pointer() : instance(0){}
    void connect(QObject * receiver, const char * slot)
    {
        QObject::connect(&emitter, SIGNAL(reset(void*)), receiver, slot);
    }
    void set(T* t)
    {
        emitter.forward(instance);
        instance = t;
    }

    //...

private:
    Emitter emitter;
    T * instance;
};

Obviously, the above code is not intended as a smart pointer implementation, just an example to show how to emit signals from a class template.

To connect a slot (say from a Form class):

Pointer<QLabel> p;
p.connect(this, SLOT(pointerreset(void*)));

The void pointer passed to the slot is the T pointer held before reset. To simplify the unavoidable cast, you can add to the Pointer class a helper method like this:

T * cast(void * p) { return static_cast<T*>(p); }

This way, for a Pointer<QLabel> pointer, in a slot:

void Form::pointerreset(void * p)
{
    QLabel * label = pointer.cast(p);
    //...
}

Depending on your smart pointer implementation, you could consider not to use the old pointer at all and just emit a signal with no arguments. Maybe there's no reason to access the old object further, especially if it is about to be deleted.