You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QML暴露继承QObject的自定义C++类报错:undefined symbol

问题解决:QML模型中QObject子类的链接错误与正确暴露方式

先解决链接错误

你遇到的undefined symbol: NetworkInterface::NetworkInterface()错误,核心原因有两个:

  1. 缺少Q_OBJECT宏:NetworkInterface继承QObject后必须添加Q_OBJECT宏,否则Qt元对象编译器(moc)不会生成必要的运行时代码,导致链接失败。
  2. 构造函数未实现:仅在头文件声明构造函数是不够的,必须在.cpp文件中提供具体实现。另外注意:QObject子类禁止自定义拷贝构造函数和赋值运算符——Qt默认会将这两个函数声明为delete,强行定义会触发未定义行为,这也是你可能踩的坑。

正确的NetworkInterface头文件+实现示例:

// NetworkInterface.h
#include <QObject>
#include <QString>

class NetworkInterface : public QObject {
    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(QString ip READ ip WRITE setIp NOTIFY ipChanged)

public:
    // 仅保留带父对象参数的构造函数(QObject推荐用法)
    explicit NetworkInterface(QObject *parent = nullptr);

    QString name() const;
    void setName(const QString &name);

    QString ip() const;
    void setIp(const QString &ip);

signals:
    void nameChanged();
    void ipChanged();

private:
    QString m_name;
    QString m_ip;
};

// NetworkInterface.cpp
#include "NetworkInterface.h"

NetworkInterface::NetworkInterface(QObject *parent) : QObject(parent) {}

QString NetworkInterface::name() const {
    return m_name;
}

void NetworkInterface::setName(const QString &name) {
    if (m_name != name) {
        m_name = name;
        emit nameChanged();
    }
}

// ip属性实现同上

再解决QML模型的类型问题

QObject子类是不可拷贝的,所以QList<NetworkInterface>这种值类型列表是非法的,Qt不允许此类用法。你需要改用以下三种合法方案:

方案1:返回QList<QObject*>作为模型

这是最简单的快速适配方案:

  • 修改Network类的getNetworkAdaptors方法:
#include <QList>
#include <QObject>

class Network : public QObject {
    Q_OBJECT
public:
    Q_INVOKABLE QList<QObject*> getNetworkAdaptors();
};
  • 实现方法时,用new创建NetworkInterface实例并绑定父对象(避免内存泄漏):
QList<QObject*> Network::getNetworkAdaptors() {
    QList<QObject*> adaptorList;
    // 假设getSystemNetworkInfo()是你获取系统网卡信息的函数
    for (const auto &info : getSystemNetworkInfo()) {
        NetworkInterface *iface = new NetworkInterface(this);
        iface->setName(info.name);
        iface->setIp(info.ip);
        adaptorList.append(iface);
    }
    return adaptorList;
}
  • QML中直接使用:
ListView {
    model: network.getNetworkAdaptors()
    delegate: Text { text: name + " : " + ip }
}

方案2:使用QQmlListProperty(符合QML属性规范)

如果需要将网卡列表作为Network类的属性暴露,推荐用这种方式:

  • 在Network类中声明属性:
#include <QQmlListProperty>

class Network : public QObject {
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<NetworkInterface> networkAdaptors READ networkAdaptors CONSTANT)

public:
    QQmlListProperty<NetworkInterface> networkAdaptors();

private:
    static int adaptorCount(QQmlListProperty<NetworkInterface> *prop);
    static NetworkInterface* adaptorAt(QQmlListProperty<NetworkInterface> *prop, int index);

    QList<NetworkInterface*> m_adaptors;
};
  • 实现回调函数和属性方法:
int Network::adaptorCount(QQmlListProperty<NetworkInterface> *prop) {
    return static_cast<QList<NetworkInterface*>*>(prop->data)->count();
}

NetworkInterface* Network::adaptorAt(QQmlListProperty<NetworkInterface> *prop, int index) {
    return static_cast<QList<NetworkInterface*>*>(prop->data)->at(index);
}

QQmlListProperty<NetworkInterface> Network::networkAdaptors() {
    // 先填充m_adaptors列表(逻辑同方案1)
    return QQmlListProperty<NetworkInterface>(this, &m_adaptors, adaptorCount, adaptorAt);
}
  • QML中直接绑定属性:
ListView {
    model: network.networkAdaptors
    delegate: Text { text: name + " : " + ip }
}

方案3:自定义QAbstractListModel(适合复杂场景)

如果需要支持模型数据更新、筛选、排序等操作,推荐自定义列表模型:

class NetworkAdaptorModel : public QAbstractListModel {
    Q_OBJECT
public:
    enum Roles {
        NameRole = Qt::UserRole + 1,
        IpRole
    };

    explicit NetworkAdaptorModel(QObject *parent = nullptr);

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    QHash<int, QByteArray> roleNames() const override;

    Q_INVOKABLE void loadAdaptors();

private:
    QList<NetworkInterface*> m_adaptors;
};
  • 核心方法实现:
int NetworkAdaptorModel::rowCount(const QModelIndex &parent) const {
    Q_UNUSED(parent);
    return m_adaptors.count();
}

QVariant NetworkAdaptorModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid() || index.row() >= m_adaptors.count())
        return QVariant();

    const NetworkInterface *iface = m_adaptors.at(index.row());
    switch (role) {
        case NameRole: return iface->name();
        case IpRole: return iface->ip();
        default: return QVariant();
    }
}

QHash<int, QByteArray> NetworkAdaptorModel::roleNames() const {
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[IpRole] = "ip";
    return roles;
}

void NetworkAdaptorModel::loadAdaptors() {
    beginResetModel();
    qDeleteAll(m_adaptors);
    m_adaptors.clear();
    // 填充数据逻辑同前
    endResetModel();
}
  • QML中使用:
NetworkAdaptorModel {
    id: adaptorModel
}

ListView {
    model: adaptorModel
    delegate: Text { text: name + " : " + ip }
}

Button {
    text: "刷新网卡"
    onClicked: adaptorModel.loadAdaptors()
}

内容的提问来源于stack exchange,提问作者user453575457

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 19:40:31