Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/laravel/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么在C++;?_C++_Memory_Memory Management_Destructor_Undefined Behavior - Fatal编程技术网

C++ 为什么在C++;?

C++ 为什么在C++;?,c++,memory,memory-management,destructor,undefined-behavior,C++,Memory,Memory Management,Destructor,Undefined Behavior,正如在第二次调用析构函数中提到的,行为12.4/14(3.8)已经是未定义的 例如: class Class { public: ~Class() {} }; // somewhere in code: { Class* object = new Class(); object->~Class(); delete object; // UB because at this point the destructor call is attempted agai

正如在第二次调用析构函数中提到的,行为12.4/14(3.8)已经是未定义的

例如:

class Class {
public:
    ~Class() {}
};
// somewhere in code:
{
    Class* object = new Class();
    object->~Class();
    delete object; // UB because at this point the destructor call is attempted again
}
在本例中,该类的设计方式是可以多次调用析构函数——不会发生类似双重删除的情况。内存仍在调用
delete
的位置分配-第一次析构函数调用不会调用
::operator delete()
来释放内存


例如,在Visual C++ 9中,上面的代码看起来很有用。即使是UB的C++定义也不会直接禁止UB的工作。因此,上述代码需要一些实现和/或平台细节


上面的代码为什么会中断?在什么条件下?

根据定义,析构函数“销毁”对象并两次销毁对象是没有意义的


您的示例可以运行,但很难正常运行

如果您调用析构函数两次,以下
类将在我的计算机上的Windows中崩溃:

class Class {
public:
    Class()
    {
        x = new int;
    }
    ~Class() 
    {
        delete x;
        x = (int*)0xbaadf00d;
    }

    int* x;
};
我可以想象一个实现,当它与微不足道的析构函数崩溃时。例如,这种实现可以从物理内存中删除已破坏的对象,对它们的任何访问都会导致某些硬件故障。看起来Visual C++不是这样的一种实现方式,但谁知道呢?

< P>标准12.4/14 < /P> 调用析构函数后 对象,对象不再存在; 如果 为对象调用析构函数 他的生命已经结束(3.8)

我认为这一节是指通过delete调用析构函数。换句话说:这一段的要点是“删除一个对象两次是未定义的行为”。所以这就是为什么您的代码示例运行良好


然而,这个问题是相当学术性的。析构函数是通过delete调用的(除了通过placement new分配的对象例外,因为sharptooth被正确地观察到)。如果您想在析构函数和第二个函数之间共享代码,只需将代码提取到一个单独的函数中,然后从析构函数调用该函数即可

我猜它被归类为未定义,因为大多数双重删除都是危险的,标准委员会不想在标准中添加例外情况,因为相对较少的情况下,它们不必这样做


至于你的代码在哪里会被破坏;您可能会在某些编译器的调试构建中发现代码中断;许多编译器将UB视为在发布模式下“对定义良好的行为执行不会影响性能的操作”,并在调试版本中“插入检查以检测不良行为”。

基本上,正如前面所指出的,对于任何执行工作的类析构函数,第二次调用析构函数都将失败。

我认为您的问题旨在说明标准背后的基本原理。从另一个角度考虑:

  • 定义两次调用析构函数的行为会产生工作,可能会产生大量工作
  • 您的示例仅表明,在一些琐碎的情况下,调用析构函数两次不会有问题。这是真的,但不是很有趣
  • 当两次调用析构函数时,您没有给出令人信服的用例(我怀疑您能),这在任何方面都是一个好主意/使代码更容易/使语言更强大/清除语义/或其他任何东西
    那么,为什么这个不会导致未定义的行为呢?

    原因是您的类可能是一个引用计数的智能指针。因此析构函数使参考计数器递减。一旦计数器达到0,实际对象应被清除

    但是如果你调用析构函数两次,那么计数就会出错

    其他情况也有同样的想法。可能析构函数将0写入内存,然后将其释放(这样您就不会意外地将用户密码留在内存中)。如果在释放内存后再次尝试写入该内存,则会出现访问冲突


    它只是让对象构造一次并销毁一次才有意义。

    这是未定义的行为,因为该标准明确了析构函数的用途,并且没有决定如果不正确使用它会发生什么。未定义的行为不一定意味着“crashy-smashy”,它只是意味着标准没有定义它,所以它由实现决定


    <> p>虽然我的C++不够流畅,但我的直觉告诉我,实现将析构函数当作另一个成员函数,或者在调用析构函数时实际销毁对象是很受欢迎的。因此,它可能会在某些实现中中断,但在其他实现中可能不会。谁知道呢,它是未定义的(如果你尝试的话,要小心恶魔从你鼻子里飞出来)。

    析构函数不是常规函数。调用一个函数并不是调用一个函数,而是调用多个函数。这就是析构函数的魔力。虽然您提供了一个微不足道的析构函数,其唯一目的是让它很难显示它可能如何中断,但您没有演示调用的其他函数的功能。标准也是如此。正是在这些功能中,事物可能会分崩离析

    作为一个简单的例子,让我们假设编译器插入代码来跟踪对象的生命周期,以便进行调试。构造函数(这也是一个神奇的函数,可以做你没有要求它做的所有事情)将一些数据存储在某个地方,上面写着“我在这里”。在调用析构函数之前,它会将这些数据更改为“我在这里”。在调用析构函数后,它将删除用于查找该数据的信息。因此,下次调用析构函数时,最终会出现访问冲突

    您可能还可以给出涉及虚拟表的示例,但您的示例代码没有包含任何虚拟函数,因此可能会