【问题标题】:Why different ways of using "new" can make result uncompatible with " delete"? [duplicate]为什么使用“new”的不同方式会使结果与“delete”不兼容? [复制]
【发布时间】:2021-08-09 18:52:34
【问题描述】:

我正在阅读 C++ Primer Plus, 12.1.3,关于内存,而关于析构函数的一些东西真的让我很困惑。

//Here is a default construtor of String class
String::String()
{
   len = 0;
   str = new char[1];
   str[0] = '\0';
}

书上说,使用str = new char[1] 而不是str = new char,这两种方式分配相同的内存,但第二种方式与析构函数不兼容。此外,书中说以下三种方式不好,因为它们与“删除”不兼容

char words[15] = "bad idea";
char *p1 = words;
char *p2 = new char;
char *p3;
delete [] p1; //undefined, so don't do it
delete [] p2; //undefined, so don't do it
delete [] p3; //undefined, so don't do it

我不知道这三种方式有什么区别,有人可以向我解释一下吗?非常感谢。

【问题讨论】:

  • delete[]new[]。你没有delete[] 你没有new[]。这里没有太多要解释的。
  • 你只能deletenew你必须delete[]new[]。也停止使用newdelete;而是使用std::vector 和/或std::make_unique
  • 如果嘘,建议你使用new/delete,那么最好的地方是在后院。如果那是你有垃圾箱的地方。
  • @Enlico 理解内存分配语义仍有一定的好处。即使很少用于更高级别的现代应用程序。
  • @DanielLangr 当然,但我会走得更远。 (非常)经常,人们陷入迭代器失效,因为他们不明白std::vector 在达到最大容量时必须重新分配内存(仅仅是因为他们甚至不明白什么是内存分配)。我坚信了解任何事物背后的机制比仅仅忽略它要好得多(更糟糕的是,将始终忽略它作为经验法则是在某些时候做愚蠢事情的最佳方法)。

标签: c++ class pointers destructor


【解决方案1】:

new char[1]分配一个长度为1的char数组,而new char分配一个char,两者有着本质的不同。

简而言之,只要您使用new,您就会使用delete 来释放分配的内存。任何时候你使用new[] 都会使用delete[] 来释放分配的内存,这就是规则。

请注意,虽然现在这是一种很好的学习练习,但还有更合适的替代方法。例如,对于动态分配的数组,您可以使用 std::vector<char> 而不是 new char[],它具有所有灵活性,没有任何缺点,例如手动内存管理。

【讨论】:

    【解决方案2】:
    char words[15] = "bad idea";
    char *p1 = words;
    char *p2 = new char;
    char *p3;
    delete [] p1; //undefined, so don't do it
    delete [] p2; //undefined, so don't do it
    delete [] p3; //undefined, so don't do it
    

    正如你提到的那本书……

    希望它也解释了为什么这三个delete[]s 是未定义的。

    如果没有,我们开始:

    char words[15] = "bad idea";
    char *p1 = words;
    delete[] p1; //undefined, so don't do it
    

    p1 没有被new char[] 分配,但获得了分配在堆栈上的数组的地址。因此,它可能不是delete[]-ed。

    char *p2 = new char;
    delete [] p2; //undefined, so don't do it
    

    p2 被分配了new。必须使用delete p2 而不是delete[] p2;。这是未定义的行为,但我听说某些编译器在后台以类似的方式处理它,甚至可以工作(可能)。

    char *p3;
    delete[] p3; //undefined, so don't do it
    

    哦,哦。 p3 是一个未初始化的指针。
    打电话给delete[] p3 是灾难性的……
    因为程序将选择一个随机地址并在其上应用其堆内存簿记。 在最好最坏的情况下,您的应用程序仍然可以运行得更远一些,直到它在神秘的条件下死掉,像std::cout << std::endl; 这样无害的东西,您将很难找出它的来源。

    如果是第三种情况

    char *p3 = nullptr;
    delete[] p3; // not undefined anymore
    

    这会使事情变得无害。
    带有空指针的delete(和delete[])是无操作且明确允许的。

    【讨论】:

    • 例如,两种情况在程序集级别的差异很大:godbolt.org/z/GYTh7f7Y7。它们可能仍然兼容,但是对于delete[],数组元素的数量是从QWORD PTR [rdi-8] 中读取的。它由new[]、(mov QWORD PTR [rax], 1) 存储在那里,但不是由new 存储。
    • @DanielLangr 你有没有为 MSVC 试过这个? (这是我听到谣言的编译器。)但是,U.B.是 U.B. - 实际上,它的未定义程度并不重要......
    • 好像是一样的:godbolt.org/z/faEEqMWaWmov QWORD PTR [rax], 1lea rbx, QWORD PTR [rcx-8])。无论如何,我知道它是 UB,但有时我认为解释在机器代码级别实际发生的事情很有用。一旦程序生成,它就会有一些由其机器代码定义的行为。只是C++标准不关心是这样的情况。
    猜你喜欢
    • 2010-12-27
    • 2019-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-23
    • 2021-12-25
    相关资源
    最近更新 更多