композиция и наследование с QObject

Смотрим предыдущий пример наследования указателя и пытаемся оставить все то же самое, но добавить наследование от QObject классов для использования технологии сигнал/слот.

main.cpp
class A
{
public:
    A():ii( 111)
    {
        qDebug("ctor A ii : %i" , ii);
    }
public:
    int ii;
};

class AA: public A
{
public:
    AA() : A()
    {
        ii = 222;
        qDebug("ctor AA  ii : %i" , ii);
    }

};

class Dlg1: public QObject
{
    Q_OBJECT

public:
    Dlg1(QObject *parent=0) : QObject (parent)
    {
        qDebug("ctor Dlg1");
        a = new A();
        init();
    }

    A *a;

    void init();

protected:

    Dlg1(A* a_, QObject *parent ) : QObject(parent), a(a_) // important : dont remember a(a_)
    {
        qDebug("protected ctor Dlg1  ii : %i" , a->ii);
        init();
    }

signals:
    void sig_1();


public slots:
    void slot_1();
};

void Dlg1::init()
{
    if ( ! connect(this , SIGNAL(sig_1()) , this , SLOT(slot_1())))
        ;
}

void Dlg1::slot_1()
{
    qDebug("Dlg1::slot_1()");
}

class Dlg2: public Dlg1
{
    //Q_OBJECT

public:

    Dlg2(QObject *parent=0 ) : Dlg1( new AA() , parent) //
    {
        qDebug("ctor Dlg2  ii : %i" , a->ii);
    }

    void foo()
    {
        qDebug("foo ii : %i" , a->ii);
    }

    //AA *aa;

};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Dlg2 dlg2;


    return a.exec();
}

....

Все реализуем в main.cpp и в этом ошибка: все собирается нормально, но не линкуется:

main.obj:-1: error: LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __thiscall Dlg1::metaObject(void)const " (?metaObject@Dlg1@@UBEPBUQMetaObject@@XZ)

Причина в том, что все классы надо разнести по самостоятельным единицам трансляции, то есть по отдельным h/cpp файлам. 

класс А
#ifndef A_H
#define A_H

#include 

class A: public QObject
{
    Q_OBJECT
public:
    A(QObject * parent = 0);

    int ii;

    void fooA();

signals:
    void sig_A();

public slots:
    void slot_A();

};

#endif // A_H

------------------------------------cpp --------------------------

#include "a.h"
#include 

A::A(QObject * parent): QObject(parent),
    ii( 111)
{
    qDebug("ctor A ii : %i" , ii);

}

void A::slot_A()
{
    qDebug("A::slot_A()");
}

void A::fooA()
{
    qDebug("sig_A()");
    emit sig_A();
}
класс АА
#ifndef AA_H
#define AA_H

#include 

#include "a.h"

class AA : public A
{
    Q_OBJECT
public:

    AA(QObject * parent =0);

public slots:

    void slot_A();

};

#endif // AA_H


-------------------------------------- cpp --------------------------------

#include "aa.h"
#include "a.h"
#include 

AA::AA(QObject * parent) : A(parent)
{
    ii = 222;
    qDebug("ctor AA  ii : %i" , ii);
}

void AA::slot_A()
{
    qDebug("AA::slot_A()");
}


класс Dlg1
#ifndef DLG1_H
#define DLG1_H

#include <QObject>

class A;

class Dlg1 : public QObject
{
    Q_OBJECT
public:

    explicit Dlg1(QObject *parent=0);

    A *a;

    void init();


    virtual ~Dlg1();

protected:

    Dlg1(A* a_, QObject *parent );

signals:

public slots:

};

#endif // DLG1_H


------------------- cpp -----------------------------------------

#include "dlg1.h"
#include "a.h"

Dlg1::Dlg1(QObject *parent) : QObject(parent), a(0)
{
    qDebug("ctor Dlg1");
    a = new A();
    init();
}

Dlg1::Dlg1(A* a_, QObject *parent ) : QObject(parent), a(a_) // important : dont remember a(a_)
{
    qDebug("protected ctor Dlg1  ii : %i" , a->ii);
    init();
}


void Dlg1::init()
{
    qDebug("Dlg1::init()");

    if ( ! connect(a , SIGNAL(sig_A()) , a , SLOT(slot_A())))
        qDebug("sig_A: connect is wrong ");
    else
        qDebug("sig_A: connect is ok");
}


Dlg1::~Dlg1()
{
    qDebug("~Dlg1()");

    if(a != 0)
        delete a;
}

....

класс Dlg2
#ifndef DLG2_H
#define DLG2_H

#include "dlg1.h"

class AA;

class Dlg2 : public Dlg1
{
    Q_OBJECT
public:
    

    explicit Dlg2( QObject *parent = 0);

    ~Dlg2();

signals:
    
public slots:

    
};

#endif // DLG2_H


-------------------- cpp -------------------------------

#include "dlg2.h"
#include "aa.h"

Dlg2::Dlg2(QObject *parent) : Dlg1( new AA() , parent)

{
    qDebug("ctor Dlg2  ii : %i" , a->ii);
}


Dlg2::~Dlg2()
{
    qDebug("~Dlg2()");

    if(a != 0)
        delete a;
    a=0;
}
main.cpp
#include <QtCore/QCoreApplication>
#include <QString>
#include <QDebug>
#include <QObject>

#include "dlg2.h"
#include "a.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Dlg2 dlg2;

    dlg2.a->fooA();

    //return a.exec();
    return 0;
}

вывод:

ctor A ii : 111
ctor AA  ii : 222
protected ctor Dlg1  ii : 222
Dlg1::init()
Object::connect: No such signal Dlg1::sig_Dlg1() in dlg1.cpp:22
sig_A: connect is ok
ctor Dlg2  ii : 222
sig_A()
AA::slot_A()
~Dlg2()
~Dlg1()

Удобство в том сигнал sig_A всегда испускается один из класса предка A (который является агрегацией в классе Dlg1),  а вот слот срабатывает в зависимости от класса с которым мы работаем либо Dlg1 либо Dlg2.

И вот скажите не является ли это наследованием агрегации ( или композиции как хотите)?

Ведь реально наследуется только класс Dlg2 от Dlg1 и AA от A.  переменная a есть агрегация в Dlg1. А в результате, мы работаем нормально и Dlg2 и с Dlg1, то есть мы переопределяем slot_A в AA ?...

Чтобы это работало мы используем два конструктора в классе A (один защищенный, хотя это не принципиально).

Мы знаем, что вариант использования конструктора определяется в классе наследнике в вызове конструктора самого наследника. Поэтому мы можем этим играть.

Кстати говоря аналогичного поведения можно добиться и просто через виртуальные функции, то есть необязательно использовать сигнал/слот Qt.




Файлы для скачивания

* вот проект на Qt 4.8.1 (у нас msvc-2010) [zip]
тестим наследование агрегации (так сказать)