Propósito del árbol de objetos
En Qt, el mecanismo de árbol de objetos permite establecer relaciones jerárquicas entre instancias de QObject mediante el método setParent. Esta estructura ofrece varias ventajas fundamentales:
- Gestión de memoria automatizada: La destrucción de un objeto padre provoca la eliminación automática de todos sus objetos hijos, evitando fugas de memoria.
- Herencia de hojas de estilo: Los estilos definidos en un objeto padre se aplican automáticamente a sus hijos, facilitando la implementación de interfaces uniformse.
- Navegación entre objetos: Los objetos pueden acceder a su padre mediante
parent()y al conjunto de hijos mediantechildren()ofindChildren().
Este artículo analiza los mecanismos internos de QObject que permiten esta gestión jerárquica.
Establecimiento de objetos padre
void QObject::establecerPadre(QObject *padre)
{
Q_D(QObject);
Q_ASSERT(!d->esWidget);
d->asignarPadreInterno(padre);
}
El método público delega la operación a una función enterna:
void QObjectPrivate::asignarPadreInterno(QObject *nuevoPadre)
{
// Verificar si ya es el padre actual
if (nuevoPadre == padreActual)
return;
// Eliminar del padre anterior si existe
if (padreActual) {
QObjectPrivate *datosPadre = padreActual->d_func();
int posicion = datosPadre->hijos.indexOf(q);
if (posicion >= 0) {
datosPadre->hijos.removeAt(posicion);
if (enviarEventos && datosPadre->recibirEventos) {
QChildEvent evento(QEvent::ChildRemoved, q);
QCoreApplication::sendEvent(padreActual, &evento);
}
}
}
// Establecer nuevo padre
padreActual = nuevoPadre;
// Agregar al nuevo padre si existe
if (nuevoPadre) {
nuevoPadre->d_func()->hijos.append(q);
if (enviarEventos && nuevoPadre->d_func()->recibirEventos) {
QChildEvent evento(QEvent::ChildAdded, q);
QCoreApplication::sendEvent(nuevoPadre, &evento);
}
}
}
Las operaciones clave incluyen: remoción del padre anetrior con notificación, actualización de la referencia al padre, y adición al nuevo padre con notificación correspondiente.
Destrucción de objetos QObject
La implementación del destructor realiza múltiples pasos para mantener la integridad del árbol:
QObject::~QObject()
{
Q_D(QObject);
d->fueEliminado = true;
// Emitir señal de destrucción (excepto widgets)
if (!d->esWidget && d->haySenalesConectadas(0)) {
emit destruido(this);
}
// Desconectar todas las conexiones de señales
QObjectPrivate::ConnectionData *conexiones = d->conexiones.loadRelaxed();
if (conexiones) {
// Desconectar receptores
int totalReceptores = conexiones->vectorSenalContador();
for (int senal = -1; senal < totalReceptores; ++senal) {
// Proceso de desconexión...
}
// Desconectar emisores
while (QObjectPrivate::Connection *nodo = conexiones->emisores) {
// Proceso de desconexión...
}
}
// Eliminar todos los hijos recursivamente
if (!d->hijos.isEmpty())
d->eliminarDescendientes();
// Removerse del objeto padre
if (d->padreActual)
d->asignarPadreInterno(nullptr);
}
La eliminación de hijos se implementa mediante:
void QObjectPrivate::eliminarDescendientes()
{
for (int i = 0; i < hijos.count(); ++i) {
QObject *hijoActual = hijos.at(i);
hijos[i] = nullptr;
delete hijoActual;
}
hijos.clear();
}
El proceso completo de destrucción incluye: emisión de señal destroyed, desconexión de todas las señales, eliminación recursiva de descendientes, y desvinculación del objeto padre.