You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

QVariant自定义类型问题:默认构造函数被调用而非拷贝构造函数

问题分析与解决办法

咱们先来拆解你遇到的两个核心问题:为什么默认构造函数被调用了两次,以及怎么才能正确拿到你创建的那个名为"Param"的Parameter对象。

一、为什么默认构造函数被调用两次?

你的代码里有两个关键问题,直接导致了这个意外的输出:

1. QVariant存的是指针,你却要提取对象

你创建QVariant的时候传的是指针:

QVariant v = QVariant::fromValue(param); // param是Parameter*类型

但后面你却尝试提取值类型Parameter

Parameter op = v.value<Parameter>();

这时候QVariant没办法把指针直接转成对象,只能默认构造一个新的Parameter实例来凑数——这就是第一次调用默认构造函数的原因。

第二次调用则和你的类继承关系有关:Parameter继承了QAbstractTableModel,而这个类是QObject的子类。Qt里QObject是设计成不能拷贝的,哪怕你自己写了拷贝构造函数,Qt的元类型系统也不会用它,反而会再次默认构造一个新对象。

2. QObject子类天生不能被拷贝

划重点:所有QObject的子类(包括QAbstractTableModel)的拷贝构造函数都是被禁用的(私有的)。你手动写的那个拷贝构造函数其实根本不会被Qt的机制调用,这也是为什么你始终看不到"copy constructor"输出的原因。

二、怎么正确获取你创建的"Param"对象?

根据你的需求,有两个靠谱的解决方向:

方案1:直接提取指针(最简单的修正)

既然你存的是指针,那提取的时候也用指针类型就好了:

Parameter *param = new Parameter("Param",0,100,10, this);
QVariant v = QVariant::fromValue(param);
// 提取指针类型,而不是值类型
Parameter *op = v.value<Parameter*>();
if(op != nullptr){
    qDebug()<< op->getName(); // 这里会输出"Param"
}

这样就不会触发默认构造函数,直接拿到你原来创建的对象指针,完美解决问题。

方案2:重构类结构(如果需要存值类型)

如果你确实需要把Parameter作为值存在QVariant里,那必须调整你的类继承:

  • QAbstractTableModel的逻辑拆出来,不要让Parameter直接继承它(因为QObject子类不能拷贝)。
  • Parameter变成一个普通的可拷贝类(不继承任何QObject子类),然后单独写一个继承QAbstractTableModel的类,把Parameter作为数据存在这个模型里。

调整后,你就能正常存值和取值,拷贝构造函数也会被正确调用:

// 假设重构后的Parameter不再继承QAbstractTableModel
Parameter param("Param",0,100,10);
QVariant v = QVariant::fromValue(param);
Parameter op = v.value<Parameter>();
qDebug()<< op.getName(); // 输出"Param",此时会打印"copy constructor"

额外小贴士

  • 自定义类型和QVariant配合时,要注意:
    • 值类型:必须可拷贝(不能是QObject子类),并且用Q_DECLARE_METATYPE注册(你已经做对了这一步)。
    • QObject子类:只能存指针,不能存值,不然会触发各种奇怪的默认构造。
  • 存QObject指针的时候,推荐用QPointer,可以避免悬空指针的问题:
QVariant v = QVariant::fromValue(QPointer<Parameter>(param));

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

火山引擎 最新活动