Administración de árboles de objetos en Qt

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 mediante children() o findChildren().

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.

Etiquetas: Qt QObject gestión-de-memoria señales-y-ranuras widgets

Publicado el 6-3 18:08