在C++QObject子类中调用析构函数之前执行操作
Perform operations BEFORE calling destructor in C++ QObject subclass
我有一个继承QObject的类层次结构。
我需要在构建之后(当对象完全构建时)和销毁之前(当对象仍然完整时)执行一些操作。
构造部分没有问题,因为我可以控制对象的构造,将其构造函数私有化,并将已经可以执行所有必需操作的创建者函数公开化。
问题出在析构函数上。我做了或多或少相同的事情:隐藏析构函数并提供一个destroyer函数,该函数执行所有操作,然后销毁对象。
这就是问题的开始:我的类层次结构被用作QJSEngine脚本模块的一部分,该模块拥有所有对象的所有权,当时间到了,它会使用QObject的析构函数来销毁它们,从而绕过我的destroyer函数。声明我的析构函数为私有是没有帮助的,因为QJSEngine总是可以执行QObject的析构因子(顺便说一句,也可以执行任何将指针投射到QObject上的代码)。
我需要在调用析构函数之前执行这些操作,因为我使用了一些虚拟函数,所以我需要在开始销毁过程之前执行这个操作,这样虚拟函数调用就不会失败。
有办法做到这一点吗?
我附上了一个显示我的问题的基本代码:
class IBase: public QObject {
public:
template<typename T>
static IBase * create() {
IBase * obj=new T;
obj->afterConstruction();
return obj;
}
void destroy() {
this->beforeDestruction();
delete this;
}
protected:
IBase(): fBeforeDestruction(false){}
virtual ~IBase(){
//Try to perform operations, but it is too late....
if (!fBeforeDestruction)
doBeforeDestruction();
}
virtual void doAfterConstruction(){}
virtual void doBeforeDestruction(){}
private:
bool fBeforeDestruction;
void afterConstruction() {
doAfterConstruction();
}
void beforeDestruction(){
fBeforeDestruction=true;
doBeforeDestruction();
}
};
class TSubclass: public IBase {
protected:
TSubclass(){}
virtual ~TSubclass(){}
virtual void doAfterConstruction(){
qDebug()<<"AfterConstruction";
}
virtual void doBeforeDestruction(){
qDebug()<<"BeforeDestruction";
}
private:
friend class IBase;
};
int main(int argc, char *argv[])
{
//QObject *obj=new TSubclass() //Compile time error! Nice!
QObject *obj=IBase::create<TSubclass>();
delete obj;//Wrong! BeforeDestruction is NEVER shown!!! <---- How to change this behaviour?
IBase * obj2=IBase::create<TSubclass>();
//delete obj2; //Compile time error! Nice!
obj2->destroy(); //Nice!
}
编辑:
在一些评论之后,我不得不补充一个问题,我想在析构函数之前做几个操作,原因有两个:
虚拟调用:在析构函数内部不允许虚拟调用,因为它们不会调用overriden函数,而只调用当前销毁类中的函数。
动态强制降频:要做的一些事情涉及通过Dynamic_cast进行降频。析构函数内部的dynamic_cast下转换总是失败。
编辑2:
Ezee的答案符合我的需要。这是我的完整代码片段,显示了代码,还有一个dynamic_cast:
template <typename T>
class TAfterConstructionBeforeDestruction: public T {
public:
~TAfterConstructionBeforeDestruction() {
this->beforeDestruction();
}
protected:
using T::T;
};
class IBase: public QObject {
public:
//Now it can be public, just as the one in QObject!
virtual ~IBase(){}
template<typename T>
static IBase * create() {
//Create a
IBase * obj=new TAfterConstructionBeforeDestruction<T>;
obj->afterConstruction();
return obj;
}
protected:
IBase(){}
virtual void afterConstruction(){}
virtual void beforeDestruction(){}
};
class TSubclass: public IBase {
public:
virtual ~TSubclass(){}
protected:
TSubclass(){}
virtual void afterConstruction(){
qDebug()<<"AfterConstruction";
}
virtual void beforeDestruction();
private:
friend class IBase;
};
class TSubclass2: public TSubclass {
public:
virtual ~TSubclass2(){}
protected:
TSubclass2(){}
virtual void beforeDestruction(){
qDebug()<<"BeforeDestruction from Subclass2";
TSubclass::beforeDestruction();
}
};
void TSubclass::beforeDestruction() {
qDebug()<<"BeforeDestruction";
TSubclass2 * sub=dynamic_cast<TSubclass2*>(this);
if (sub) {
qDebug()<<"We are actually a TSubclass2!";
}
}
int main(int argc, char *argv[])
{
//QObject *obj=new TSubclass() //Compile time error! Nice!
QObject *obj=IBase::create<TSubclass>();
delete obj;//Now it works fine!
IBase * obj2=IBase::create<TSubclass2>();
delete obj2; //It is still succeeding to dynamic_cast to TSubclass2 without any problem!
}
首先,我必须说,从构造函数或析构函数调用虚拟方法是一种非常糟糕的做法。
- 从IBase的mose派生后代的构造函数中调用
doAfterConstruction()
- 从IBase的mose派生后代的析构函数调用
doBeforeDestruction()
您可以使用信号/插槽进行同样的操作:
- 在
IBase
中声明信号beforeDestroyed()
(同时添加Q_OBJECT宏) - 在
IBase
的构造函数中,将该信号连接到槽doBeforeDestruction
(使其成为槽) - 在IBase的mose派生子代的析构函数中发出信号:
emit beforeDestroyed()
如果你有很多后代,你可能希望避免在每个构造函数/析构函数中做同样的事情。在这种情况下,你也可以使用一个模板:
template <class T>
class FirstAndLastCall : public T
{
public:
FirstAndLastCall ()
{
doAfterConstruction();
}
~FirstAndLastCall
{
doBeforeDestruction();
}
}
Usage:
IBase* obj2 = new FirstAndLastCall<TSubclass>();
- 按下Arduino按钮后,如何在C#应用程序上执行操作
- 是否可以访问类数据成员并在析构函数中对它们执行操作?
- 如何让派生类在调用基类之前执行操作?
- CListCtrl 根据选定的行值执行操作
- 在服务器上执行操作的正确和 REST 方式?
- 是否可以拦截/检测关闭 PC 并首先执行操作的请求?
- 如何有效地使用 std::async 对指针数组执行操作
- 无法对套接字执行操作,因为系统缺少足够的缓冲区空间或队列已满
- 递归推动POP POSTFIX计算器无法正确执行操作,只需将最后一个数字作为结果
- QListWidgetItem 当按钮单击时执行操作
- 为什么C++没有简洁的语法允许在不定义计数器的情况下多次执行操作?
- 如何在标准库C/C++函数中进行gdb单步执行操作
- 如何对 n 个字符串执行操作,然后在C++中打印它们
- 从文件中读取运算符并执行操作C++
- 使用重载运算符在函数调用时执行操作
- 您如何选择在opencv-dnn上执行操作的设备
- 如何使用BoostSpirit.Qi增量解析(并对其执行操作)大文件
- 在remove_if删除元素之前,我可以滥用谓词对元素执行操作吗
- 在使用Qt修改任何复选框状态时执行操作
- 是否可以在不使用任何头文件的情况下在C++中执行I/O操作