gpt4 book ai didi

c++ - 在 C++ 中,当重新分配一个对象时,为什么构造函数在析构函数之前触发?

转载 作者:行者123 更新时间:2023-11-28 04:10:41 27 4
gpt4 key购买 nike

<分区>

我是 C++ 的初学者。我正在使用 GCC 9.2.0 编译我的代码。最近我偶然发现了一些奇怪的行为,这让我陷入了段错误。

我有一个特定的 Hyperscan 类包装器。我已经做到了,所以它基本上具有与 Python 的 re 相同的功能,但速度快很多倍并且是多线程的。我有一个 regex 类,我通常这样分配

auto some_regex = regex();

现在,对于那些不熟悉 Hyperscan 的人来说,构造函数的作用是 2(实际上是 3)件事 -> 首先它为正则表达式数据库分配内存。这是通过调用他们在 C 中的 API 来完成的。然后,如果成功,您将分配暂存空间。现在,因为我的实现支持多线程,所以我的类实例包含一个成员,它是一个暂存空间 vector 。尽管如此,我还是为我使用的每个线程分配了一个暂存空间,还有它们的 API。然后我得到一个指向已编译正则表达式数据库的指针和一个临时空间指针 vector 。

解构对象时,我必须释放数据库和暂存空间(我还使用 API 提供的函数)(如果已分配)。到目前为止,一切都很好。当我做这样的事情时,问题就出现了:

auto some_regex = regex();
some_regex = regex(R"(\s+)");

你看到这里有问题了吗?也许我太菜鸟看不到它,但它对我来说看起来不错。但是没有。它在试图释放数据库内存时在析构函数中出现段错误。调试它,我遇到了这些操作:

allocate memory for some_regex
initialize some_regex
allocated new memory for some_regex
initialize new some_regex
run destructor for the old some_regex
run destructor for the new some_regex

基本上,第一个实例的析构函数仅在第二个实例的构造函数触发后运行。这实际上是为了让第一个析构函数释放由第二个构造函数分配和初始化的数据,而不是仅仅清理自身。

现在,事情变得更奇怪了。我查看了手头的地址,因为我开始将释放的指针设置为 nullptr - 结果发生了这种情况:

allocate memory for some_regex
initialize some_regex
allocated new memory for some_regex
initialize new some_regex
run destructor for the old some_regex
database pointer is not 0, free it
set database pointer to nullptr
run destructor for the new some_regex
database pointer is not 0, free it (BUT THIS IS ALREADY FREED MEMORY???)
SIGSEGV

我完全糊涂了。即使在将数据库指针显式设置为 null 之后,新指针也会在构造函数完成其工作后为其分配一个新地址。

这重现了行为:

#include <iostream>
#include <malloc.h>

mock::mock()
{
std::cout << "Starting constructor..." << std::endl;
this->test = (int*)malloc(sizeof(int));
*this->test = 0;
std::cout << "Ending constructor..." << std::endl;
}

mock::mock(int x)
{
std::cout << "Starting full constructor..." << std::endl;
this->test = (int*)malloc(sizeof(int));
*this->test = x;
std::cout << "Ending full constructor..." << std::endl;
}

mock::~mock()
{
std::cout << "Starting destructor...";
free(this->test);
this->test = nullptr;
std::cout << "Address of test is " << this->test << std::endl;
std::cout << "Ending destructor..." << std::endl;
}

像这样在 main 中运行它:

auto some_mock = mock();
some_mock = mock();
some_mock = mock();
some_mock = mock(3);

预期结果:

Starting constructor...
Ending constructor...
Starting destructor...Address of test is 0
Ending destructor...
Starting constructor...
Ending constructor...
Starting destructor...Address of test is 0
Ending destructor...
Starting constructor...
Ending constructor...
Starting destructor...Address of test is 0
Ending destructor...
Starting full constructor...
Ending full constructor...
Starting destructor...Address of test is 0
Ending destructor...
Process returned 0

实际结果:

Starting constructor...
Ending constructor...
Starting constructor...
Ending constructor...
Starting destructor...Address of test is 0
Ending destructor...
Starting constructor...
Ending constructor...
Starting destructor...Address of test is 0
Ending destructor...
Starting full constructor...
Ending full constructor...
Starting destructor...Address of test is 0
Ending destructor...
*** Error in `/deploy/cmake-build-local-debug/CPP': double free or corruption (fasttop): 0x0000000002027c40 ***
.
.
.
Starting destructor...
Process finished with exit code 134 (interrupted by signal 6: SIGABRT)

这是有意为之的行为吗?如果是这样,为什么???我怎样才能解决这个问题(除了在重新分配之前使用新的和删除之外)?

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com