在构造函数中调用析构或者delete的区别

本文最后更新于:3 个月前

偶然在一篇面经中看到了这个屌问题,因此做了个测试来验证一下不同情况下会发生什么。测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 struct ListNode {
int val;

ListNode(int _val) : val(_val) {
//delete this;
//this->~ListNode();
cout << val << endl;
cout << "abc";
}

};

int main() {

//ListNode* a = new ListNode(1);

//ListNode a(1);

return 0;
}

对象在堆上

在构造函数中调用 delete this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 struct ListNode {
int val;

ListNode(int _val) : val(_val) {
delete this;
//this->~ListNode();
cout << val << endl;
cout << "abc";
}

};

int main() {

ListNode* a = new ListNode(1);

//ListNode a(1);

return 0;
}

在debug模式下运行结果:

image-20230823204058996

在release模式下运行结果:

image-20230823204141122

解释:delete包含两个操作,首先调用对象的析构函数,其次free将相关内存释放掉。在debug模式下,会将free掉的内存统一用dd来覆盖掉,作为一种mask标记,如果别的指针指向并读取了相关内存就会报错。在调试模式下打断点结果如下:

image-20230823204444262

但是在release模式下free不会重写内存,只会将该内存块还给内存池。因此如果这段内存没有被其它线程申请,那么可以读到原始值的。

在构造函数中调用析构函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 struct ListNode {
int val;

ListNode(int _val) : val(_val) {
delete this;
//this->~ListNode();
cout << val << endl;
cout << "abc";
}

};

int main() {

ListNode* a = new ListNode(1);

//ListNode a(1);

return 0;
}

在debug模式和release模式下输出都是正常的:

image-20230823204712896

这是由于类中数据类型都是POD(plain old data)的,因此析构函数就是个空函数,直接忽略,输出正常。

对象在栈上

这里不区分debug和release了,结果是一样的。

在构造函数中调用 delete this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 struct ListNode {
int val;

ListNode(int _val) : val(_val) {
delete this;
//this->~ListNode();
cout << val << endl;
cout << "abc";
}

};

int main() {
//ListNode* a = new ListNode(1);

ListNode a(1);
return 0;
}

结果直接崩了,报错:

image-20230823210048383

哦哦,对,这个不是new出来的,那如果直接free(this)呢?还是崩了。因此对栈上的东西手动去做delete或者free本来就是错误的,没什么讨论的意义。

在构造函数中调用析构函数

image-20230823205624363

效果正常,和上面的解释一样,析构函数属于什么都没做,可以正常输出,无论堆栈。