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子类),并且用
- 存QObject指针的时候,推荐用
QPointer,可以避免悬空指针的问题:
QVariant v = QVariant::fromValue(QPointer<Parameter>(param));
内容的提问来源于stack exchange,提问作者Juliette Marquis




