[QT Creator源码笔记] Aggregation
#pragma once
#include <qglobal.h>
#if defined(AGGREGATION_LIBRARY)
# define AGGREGATION_EXPORT Q_DECL_EXPORT
#else
# define AGGREGATION_EXPORT Q_DECL_IMPORT
#endif
#pragma once
#include "aggregation_global.h"
#include <QObject>
#include <QList>
#include <QHash>
#include <QReadWriteLock>
#include <QReadLocker>
namespace Aggregation {
// Aggregate 看成一个部门
// component 看成一个 部门下的一个成员
// m_components 看成一个部门拥有的所有成员
// aggregateMap 看成 记录所有单个成员和对应部门的总表
// 在做查询时,能找到直接返回该类型的成员,找不到就一路找到 Aggregate
// 然后用 Aggregate 中query去查询其他的成员,如过存在想要的成员,就输出
class AGGREGATION_EXPORT Aggregate : public QObject
{
Q_OBJECT
public:
Aggregate(QObject *parent = nullptr);
~Aggregate() override;
void add(QObject *component);
void remove(QObject *component);
template <typename T> T *component() {
QReadLocker locker(&lock());
for (QObject *component : qAsConst(m_components)) {
if (T *result = qobject_cast<T *>(component))
return result;
}
return nullptr;
}
template <typename T> QList<T *> components() {
QReadLocker locker(&lock());
QList<T *> results;
for (QObject *component : qAsConst(m_components)) {
if (T *result = qobject_cast<T *>(component)) {
results << result;
}
}
return results;
}
static Aggregate *parentAggregate(QObject *obj);
static QReadWriteLock &lock();
signals:
void changed();
private:
void deleteSelf(QObject *obj);
static QHash<QObject *, Aggregate *> &aggregateMap();
QList<QObject *> m_components;
};
// get a component via global template function
template <typename T> T *query(Aggregate *obj)
{
if (!obj)
return nullptr;
return obj->template component<T>();
}
template <typename T> T *query(QObject *obj)
{
if (!obj)
return nullptr;
T *result = qobject_cast<T *>(obj);
if (!result) {
QReadLocker locker(&Aggregate::lock());
Aggregate *parentAggregation = Aggregate::parentAggregate(obj);
// query<T>(parentAggregation) 调用 template <typename T> T *query(Aggregate *obj)
// 在 Aggregate 中的 m_components查找类型匹配的
result = (parentAggregation ? query<T>(parentAggregation) : nullptr);
}
return result;
}
// get all components of a specific type via template function
template <typename T> QList<T *> query_all(Aggregate *obj)
{
if (!obj)
return QList<T *>();
return obj->template components<T>();
}
template <typename T> QList<T *> query_all(QObject *obj)
{
if (!obj)
return QList<T *>();
QReadLocker locker(&Aggregate::lock());
Aggregate *parentAggregation = Aggregate::parentAggregate(obj);
QList<T *> results;
if (parentAggregation)
results = query_all<T>(parentAggregation);
else if (T *result = qobject_cast<T *>(obj))
results.append(result);
return results;
}
} // namespace Aggregation
using namespace Aggregation;
/*!
Returns the aggregate object of \a obj if there is one. Otherwise returns 0.
*/
Aggregate *Aggregate::parentAggregate(QObject *obj)
{
QReadLocker locker(&lock());
return aggregateMap().value(obj);
}
QHash<QObject *, Aggregate *> &Aggregate::aggregateMap()
{
static QHash<QObject *, Aggregate *> map;
return map;
}
/*!
\internal
*/
QReadWriteLock &Aggregate::lock()
{
static QReadWriteLock lock;
return lock;
}
/*!
Creates a new aggregate with the given \a parent.
The parent is directly passed to the QObject part
of the class and is not used beside that.
*/
Aggregate::Aggregate(QObject *parent)
: QObject(parent)
{
QWriteLocker locker(&lock());
aggregateMap().insert(this, this);
}
/*!
Deleting the aggregate automatically deletes all its components.
*/
Aggregate::~Aggregate()
{
QList<QObject *> components;
{
QWriteLocker locker(&lock());
for (QObject *component : qAsConst(m_components)) {
disconnect(component, &QObject::destroyed, this, &Aggregate::deleteSelf);
aggregateMap().remove(component);
}
components = m_components;
m_components.clear();
aggregateMap().remove(this);
}
qDeleteAll(components);
}
void Aggregate::deleteSelf(QObject *obj)
{
{
QWriteLocker locker(&lock());
aggregateMap().remove(obj);
m_components.removeAll(obj);
}
delete this;
}
/*!
Adds the \a component to the aggregate.
You cannot add a component that is part of a different aggregate
or an aggregate itself.
\sa remove()
*/
void Aggregate::add(QObject *component)
{
if (!component)
return;
{
QWriteLocker locker(&lock());
Aggregate *parentAggregation = aggregateMap().value(component);
if (parentAggregation == this)
return;
if (parentAggregation) {
qWarning() << "Cannot add a component that belongs to a different aggregate" << component;
return;
}
m_components.append(component);
connect(component, &QObject::destroyed, this, &Aggregate::deleteSelf);
aggregateMap().insert(component, this);
}
emit changed();
}
/*!
Removes the \a component from the aggregate.
\sa add()
*/
void Aggregate::remove(QObject *component)
{
if (!component)
return;
{
QWriteLocker locker(&lock());
aggregateMap().remove(component);
m_components.removeAll(component);
disconnect(component, &QObject::destroyed, this, &Aggregate::deleteSelf);
}
emit changed();
}
测试
#pragma once
#include <aggregate.h>
#include <QString>
class IComboEntry : public QObject
{
Q_OBJECT
public:
IComboEntry(QString title) : m_title(title) {}
virtual ~IComboEntry() {}
QString title() const { return m_title; }
private:
QString m_title;
};
class IText1 : public QObject
{
Q_OBJECT
public:
IText1(QString text) : m_text(text) {}
virtual ~IText1() {}
QString text() const { return m_text; }
private:
QString m_text;
};
class IText2 : public QObject
{
Q_OBJECT
public:
IText2(QString text) : m_text(text) {}
QString text() const { return m_text; }
private:
QString m_text;
};
class IText3 : public QObject
{
Q_OBJECT
public:
IText3(QString text) : m_text(text) {}
virtual ~IText3() {}
QString text() const { return m_text; }
private:
QString m_text;
};
#pragma once
#include "myinterfaces.h"
#include "ui_main.h"
#include <aggregate.h>
#include <QWidget>
class MyMain : public QWidget
{
Q_OBJECT
public:
explicit MyMain(QWidget *parent = 0);
~MyMain();
void add(IComboEntry *obj);
private:
void select(int index);
Ui::mainClass ui;
QList<IComboEntry *> m_entries;
};
#include "main.h"
#include <QApplication>
MyMain::MyMain(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
connect(ui.comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MyMain::select);
}
void MyMain::add(IComboEntry *obj)
{
m_entries.append(obj);
ui.comboBox->addItem(obj->title());
}
void MyMain::select(int index)
{
IComboEntry *entry = m_entries.at(index);
// with multiple inheritance we would use qobject_cast here
// instead we use query, to get the components if they exist
IText1 *t1 = Aggregation::query<IText1>(entry); //转化失败就去找是不是挂载到同一个Aggregate下面
IText2 *t2 = Aggregation::query<IText2>(entry);
IText3 *t3 = Aggregation::query<IText3>(entry);
// set the label texts and enable/disable, depending on whether
// the respective interface implementations exist
ui.text1->setText(t1 ? t1->text() : tr("N/A"));
ui.text2->setText(t2 ? t2->text() : tr("N/A"));
ui.text3->setText(t3 ? t3->text() : tr("N/A"));
ui.text1->setEnabled(t1);
ui.text2->setEnabled(t2);
ui.text3->setEnabled(t3);
}
MyMain::~MyMain()
{
// the following deletes all the Aggregate and IComboEntry and ITextX
// objects, as well as any other components we might have added
qDeleteAll(m_entries);
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyMain w;
// create and set up some objects
// the first does only implement the required IComboEntry
// we don't need an aggregation for this
IComboEntry *obj1 = new IComboEntry("Entry without text");
// the second additionally provides an IText2 implementation
// adding a component to the aggregation is done by setting the aggregation as the parent of the component
Aggregation::Aggregate *obj2 = new Aggregation::Aggregate;
obj2->add(new IComboEntry("Entry with text 2"));
obj2->add(new IText2("This is a text for label 2"));
// and so on... two more objects...
Aggregation::Aggregate *obj3 = new Aggregation::Aggregate;
obj3->add(new IComboEntry("Entry with text 1 and 2"));
obj3->add(new IText1("I love Qt!"));
obj3->add(new IText2("There are software companies..."));
Aggregation::Aggregate *obj4 = new Aggregation::Aggregate;
obj4->add(new IComboEntry("Entry with text 1 and 3"));
obj4->add(new IText1("Some text written here."));
obj4->add(new IText3("I'm a troll."));
// the API takes IComboEntries, so we convert the them to it
// the MyMain object takes the ownership of the whole aggregations
w.add(Aggregation::query<IComboEntry>(obj1)); //要么是 IComboEntry 要么是挂载的上一级
w.add(Aggregation::query<IComboEntry>(obj2));
w.add(Aggregation::query<IComboEntry>(obj3));
w.add(Aggregation::query<IComboEntry>(obj4));
w.show();
return app.exec();
}
测试结果
![](https://img-blog.csdnimg.cn/a52bb3b032a945678452ceca233ae1ff.png)
![](https://img-blog.csdnimg.cn/3d9d342d6cdf4cf7889cabfab60bf62a.png)
![](https://img-blog.csdnimg.cn/20a9206a5c994085b7267280e02422c3.png)
![](https://img-blog.csdnimg.cn/d6203b5fe64247afbc61a493f508c4c2.png)