当前位置: 首页 > 知识库问答 >
问题:

使用新位置时未定义的行为或内存泄漏

萧展鹏
2023-03-14

我正在使用此处列出的书籍学习C中的放置新。现在,为了查看一些示例,我在SO帖子中发现了以下片段,声称它(给定示例)具有未定义的行为:

例如,这有UB:

void ub() {
   alignas(string) char buf[sizeof(string)]; // memory is allocated
   new(buf) string("1");                     // string("1") is constructed
} // memory is deallocated but string("1") outlives the memory!

如您所见,用户声称上面的片段有UB。但是我认为它有内存泄漏,而不是UB。有人能告诉我上面的片段是有UB还是内存泄漏,或者两者兼而有之,我的理解(它有内存但没有UB)是否正确。

共有3个答案

司空实
2023-03-14

人们可以争论任何一种方式。大多数时候,这根本不重要,尽管显然越好(更安全,记忆

让我们想象一下,您有一个具有成员int foosOutlived的Foo类,如下所示:

static std::list<Foo*> ALL_MY_FOO;

Foo::Foo()
 : foosOutlived(0)
{
    ALL_MY_FOO.push_back(this);
}

Foo::~Foo()
{
    ALL_MY_FOO.remove(this);
    for (Foo* aFoo : ALL_MY_FOO)
        aFoo->foosOutlived += 1;
}  

如果内存被释放,并且您无法在此处调用析构函数,那么您将在ALL\u MY\u FOO中保留一个悬空指针。。。

程序可以通过重用对象占用的存储或显式调用对象的析构函数或伪析构函数来结束任何对象的生存期<对于类类型的对象,在重用或释放该对象占用的存储之前,程序不需要显式调用析构函数;但是,如果没有显式调用析构函数,或者如果没有使用删除表达式来释放存储,则不会隐式调用析构函数,并且依赖析构函数产生的副作用的任何程序都具有未定义的行为。

突出的一点似乎是:程序是否依赖于析构函数产生的副作用?那么不调用析构函数就是UB。

陶山
2023-03-14

在这种情况下,除了明显的内存泄漏之外,您的代码没有真正的问题。

不过,您构造的类型的构造函数可能有副作用,比如将构造的对象添加到所有此类对象的全局列表中。然后,析构函数将从所述列表中删除该对象,但您永远不会调用析构函数。因此,全局列表以一个悬空指针结束。

注意:现代C有construct_at来替换您的新位置。

祁通
2023-03-14

标准中有一句话不太清楚[basic.life]/5中的含义,说如果像您引用的示例中那样省略了析构函数调用,那么

任何依赖于析构函数产生的副作用的程序都有未定义的行为。

不清楚这里的“依赖于副作用”是什么意思。如果你认为泄漏内存的副作用是你的程序“依赖”的东西,那么也许它适用,但我怀疑这是预期的阅读。

《化学武器公约》第2523期建议删除这一短语,代之以非规范性注释,提及在这种情况下不呼叫析构函数的潜在问题。另请参阅此处的讨论。

除此之外,没有未定义的行为,只有内存泄漏。当然,对于除字符串之外的其他类型,如果在释放其内存之前没有正确调用析构函数,则很容易导致未定义的行为。

在实践中,您不应该让这种情况发生,即使只是为了避免内存泄漏。因此,您实际上可以将其视为未定义的行为。

 类似资料:
  • 我有一个包含字符串的结构,该结构用于向量。当向量增长时,所有元素都移动到新的分配。不幸的是,这一举动还导致std::string内存泄漏。 以下是一些最小可重复的情况。第一个示例将说明内存泄漏发生的位置,但可以对此做出判断。第二个例子将涵盖困扰我的内存泄漏。第三个将更进一步。最后,我将展示实际用例来演示我正在做什么,以及为什么我会问这个问题。 不出所料,这会导致内存泄漏。构建开始字符串,并为数据分

  • 我在继续我的游戏超过8次后,我得到了OutOfMemory错误,因为堆逐渐填充。在使用MAT分析我的游戏堆时,我知道以下2个原因: 关键词Android.Graphics.Bitmap Byte[] 关键词java.lang.Object[]Android.content.res.resources 请提出解决方案

  • 尽管标题出现了,但这并不是一个哲学问题。 从未初始化的数组读取 使用错误数据 使用不可移植构造。(即内存分配的细节1) 导致具有的行为 标准没有要求产生可预测的效果 我会称之为“未定义的行为”。但也许我错过了什么(?) null null

  • 我在Eclipse中使用Lifeay IDE创建了一个新项目(带有插件Portlet类型和JSF2.x.x Portlet框架的Liferay项目)。我没有做任何更改,将project添加到LiferayV6.1CE服务器(Tomcat7)并启动了这个服务器。 不幸的是,我得到了这个错误(portlet未注册)。 (...)09:05:08,828信息[ContainerBackgroundPro

  • 问题内容: 由于这是我第一次学习系统编程,因此我很难将规则束之高阁。现在,我对内存泄漏感到困惑。让我们考虑一个例子。说,Rust正在抛出一个Python将会捕获的指针(指向字符串)。 在Rust中,(我只是发送的指针) 在Python中,(我取消引用了指针) 现在,我的问题是释放内存。我认为应该在Python中将其释放,但随后所有权会突然增加。因为,似乎采用了不可变的参考。因此,我对于是否应该在R

  • 我有一个Java应用程序,它合并了一些大文件。Java应用程序不在我的控制之下。Java应用程序的结果以大约90MB的大字符串返回给我的C程序,我在其中的一些算法中使用它。我多次调用execute方法。我的问题是,每次调用Java应用程序时,它都会保留更多内存,但不会释放内存。从这个垃圾收集和JNI调用问题中,我想到手动调用垃圾收集器,但它不会释放内存。有没有办法解决这个问题? 这是我的C程序