【问题标题】:Can I call placement-new and the destructor through references?我可以通过引用调用placement-new 和析构函数吗?
【发布时间】:2018-06-06 21:18:14
【问题描述】:

我可以传递对未初始化内存的引用,获取地址,然后调用placement-new 或在获得的内存位置上调用析构函数吗?换句话说,以下程序是合法的 C++ 还是具有未定义行为:

#include <string>
#include <iostream>

void ctor (std::string &s)
{
  new (&s) std::string ("Hello, world!");
}

void dtor (std::string &s)
{
  (&s)->std::string::~string ();
}

int main ()
{
  std::string * memory = static_cast<std::string *> (
    operator new (sizeof (std::string) * 10));

  ctor (memory [0]);

  std::cout << memory [0] << '\n';

  dtor (memory [0]);

  operator delete (memory);
}

当然可以,而且我尝试了 gcc 的未定义行为清理程序,它没有产生任何错误。但是任何人都可以根据标准确认/反驳。

【问题讨论】:

  • @CaptainObvlious 是的,这不是本意。谢谢!
  • 您可以使用 static_cast 代替。并且没有必要将参数转换为operator delete
  • @M.M 是的,这样更好。谢谢!

标签: c++ pointers reference placement-new


【解决方案1】:

上面的代码在[0]处双重构造一个字符串,这是一个潜在的资源泄漏,并双重破坏它,这是未定义的行为。

new string[10] 在数组中构造对象。 delete[] 销毁对象。你在另一个之上创建一个新对象,然后销毁它,然后在 delete[] 时再次销毁它。

看起来没有其他问题;我的意思是它使用原始分配,这通常是不好的做法,并且代码是异常不安全的,等等。

【讨论】:

  • 问题似乎已被编辑,因此new[] 不再使用...
  • @Yakk 好吧,问题是“我可以通过引用调用placement-new 和析构函数吗?”。该代码只是为了使其尽可能精确。即使没有代码,我觉得问题已经足够清楚了。而且,是的,我在原始代码中犯了一个令人尴尬的错误。那好吧。很高兴,我对编写这种代码不太熟悉,所以会发生错误。但是,也许你是对的,我应该提出一个新问题;不过,不确定是否允许有相同标题的问题。
  • @Yakk 所以,我无法添加具有相同标题的新问题,也无法删除此问题,因为“其他人已经投入了时间”。我觉得这个问题的标题很合适。您认为我是否应该将“通过”替换为“通过”并再次提出问题?
  • @Yakk 问“我怎么能打电话给......”意味着它是可能的。问题的全部意义在于询问是否有可能。很高兴您更改代码以摆脱与实际问题无关的任何 UB。但我明白了:你刚刚看到一段代码并进入了语言律师模式。虽然这可能会满足你做对的冲动,但这并不是很有帮助。
  • @Yakk 我认为你不必读懂我的想法;只是问题的标题和第一句话。 ;) 说我明确询问“以下程序是否正常”而不考虑“换句话说”充其量是不真诚的。你真的认为如果我用手动 cmets 替换内存分配和释放会是一个更好的问题吗?无论如何,我现在认为您根本不知道实际问题的答案,这就是您难以捉摸的原因。 ;)
【解决方案2】:
dtor (memory [0]);

delete [] memory;

绝对是未定义的行为。

在该行中构造的对象的生命周期

std::string * memory = new std::string [10];

在您使用placement new 运算符时结束。

因此,

delete [] memory;

是未定义的行为。

更新

更新的代码行

dtor (memory [0]);

operator delete (reinterpret_cast<void *> (memory));

表现良好。

使用placement new 操作符分配的对象的析构函数只被调用一次。

operator new 调用分配的内存被operator delete 调用释放。

【讨论】:

  • 问题似乎已被编辑,因此delete[] 不再使用...
【解决方案3】:

编辑后,似乎不再有未定义的行为。

但是,您的代码中仍然存在危险
您的memory 变量具有指向构造 字符串的指针类型,即使在指针初始化后该指针后面没有字符串。并且指针也不是nullptr。那很危险。您正在向编译器断言某些东西是正确构造的对象,但事实并非如此。因此,您的编译器不会发现您错误地使用未构造的对象。

我强烈建议使用char* 变量跟踪未初始化的内存,并且只对已经正确构造的对象使用类类型指针。

【讨论】:

  • 谢谢!但它实际上纯粹是语言律师问题,而不是关于这是否是一个应该编写的代码的问题(我知道,一个不应该)。所以在这方面,我觉得答案没有抓住重点。你能解释一下,为什么这不是未定义的。可以参考一下标准吗?
  • 你能解释一下“向编译器断言”吗?
  • @MM 对于“断言编译器”,我的意思只是说:“听着,编译器,我知道你认为这堆字节只是一堆字节,但我,程序员,你的上帝,告诉你这真的是一个std::string 对象。所以,现在停止抱怨不兼容的类型!”这就是 std::string* 的强制转换与将指针存储在类型为 std::string* 的变量中相结合的作用。
猜你喜欢
  • 2014-03-10
  • 2021-11-15
  • 2021-08-31
  • 2018-05-13
  • 2023-03-22
  • 2014-04-15
  • 2014-08-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多