QML暴露继承QObject的自定义C++类报错:undefined symbol
问题解决:QML模型中QObject子类的链接错误与正确暴露方式
先解决链接错误
你遇到的undefined symbol: NetworkInterface::NetworkInterface()错误,核心原因有两个:
- 缺少
Q_OBJECT宏:NetworkInterface继承QObject后必须添加Q_OBJECT宏,否则Qt元对象编译器(moc)不会生成必要的运行时代码,导致链接失败。 - 构造函数未实现:仅在头文件声明构造函数是不够的,必须在.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
相关产品推荐
相关产品推荐

