【问题标题】:explicit call to destructor is not destroying my object why?显式调用析构函数不会破坏我的对象,为什么?
【发布时间】:2011-11-15 19:11:49
【问题描述】:

我正在调用析构函数来释放内存,但它并没有删除我的对象。背后的原因是什么?

我的代码是这样的:

class A
{
public: 
    int a;
    A()
    {
        cout << "a" << endl;
    }
};

class B :public A
{
public: 
    int b;
    B()
    {
        cout << "b" << endl; a = 10; b = 20;
    }
    ~B()
    {
        cout << a << b << endl;
    }
};

我正在使用它:

int main()
{
    {
        B b;
        b.~B();
        b.b=100;  // why this step is executed?
    }
    int x;
    cin>>x;
    return 0;
}

【问题讨论】:

  • 你只调用了析构函数,没有使用delete。这里没有释放内存。
  • 你到底想做什么?除了在放置new 后清理之外,没有太多理由明确销毁对象...
  • 你为什么一开始就显式调用析构函数?你期望会发生什么?该对象是在堆栈上创建的,并且在您退出声明它的范围之前不会被弹出,因此在您的微不足道的情况下,即使您在调用析构函数后对该对象进行操作,也不会发生任何坏事。此外,在执行 b.b=100; 之后,编译器将再次调用析构函数,因为那是 b 生命周期的结束(由 {} 划分)。

标签: c++ destructor


【解决方案1】:

我正在调用析构函数来释放内存

为什么?在语言级别,析构函数不会释放对象本身占用的内存。

一个重要的析构函数结束对象的生命周期,但它不会结束对象的存储持续时间。这意味着内存仍然分配,它只是变成“原始”(未初始化)。 所以,从这个意义上说,它是破坏你的对象。

同时,一个微不足道的析构函数根本没有任何作用。即使您显式调用它,对象的生命周期也不会结束。

在您的情况下,析构函数B::~B 并不重要,这正式意味着通过调用它您结束了对象的生命周期。你破坏了它,就像一个本地对象可以被破坏一样。 But the memory remains。尝试以 B 对象的形式访问该内存只会导致未定义的行为。

事实上,没有办法手动释放本地对象占用的内存。本地内存总是自动释放。

【讨论】:

  • 太糟糕了,视频已被删除:\
  • @AnT:我试过ideone.com/Oz0yoF这个程序。它给了我正确的值 9 作为结果 + 2 个析构函数调用。那么,我可以说一个对象的内存仍然分配给一个测试类对象,但它可能会变成“原始”。因此,在显式调用析构函数 & 之后再次调用 t.get_a() 是未定义的行为,当随后以通常会调用对象隐式销毁的方式留下块时。对吗?
  • @Pravasi Meet:您的对象的存储持续时间不会被析构函数调用终止,这意味着内存仍在分配中。但是对象的生命周期已经结束,这意味着在它上面调用get_a()是未定义的。
  • @AnT 你太棒了,很多事情都从这个答案中得到了解决。谢谢
  • @AnT there's no way to manually deallocate memory occupied by a local object 我知道了,但是 global objectstatic(keyword) objects 我认为我们不能取消分配还有手动记忆。所以我认为只有在堆中创建的内存(借助 new 关键字动态声明的对象)我们可以手动删除,对吗?
【解决方案2】:

你确实调用这样的析构函数(嗯,你可以但通常不会这样做)。

对于像b 这样的自动变量,当变量超出范围时,析构函数将被调用。您永远不需要显式调用析构函数。

对于使用new 在堆上分配的对象,析构函数将在您delete 它们之后调用。在这种情况下,您也不必显式调用析构函数。

12.4 Destructors 中的 C++03 状态:

隐式调用析构函数:

  • 对于在程序终止时具有静态存储持续时间 (3.7.1) 的构造对象;
  • 对于具有自动存储持续时间 (3.7.2) 的构造对象,当创建对象的块退出时;
  • 当临时对象的生命周期结束时构造的临时对象;
  • 对于由 new 表达式分配的构造对象,通过使用 delete 表达式;
  • 在几种情况下由于异常处理。

也可以显式调用析构函数。

注意:很少需要显式调用析构函数。这种调用的一种用途是使用带有放置选项的新表达式放置在特定地址的对象。为了处理专用硬件资源和编写内存管理工具,可能需要对对象进行显式放置和销毁。

尤其是不要做你想做的事,因为析构函数会被调用两次,一次是由你显式调用,一次是在b超出范围时隐式调用。来自标准的同一部分:

一旦为对象调用析构函数,该对象就不再存在;如果为生命周期已结束的对象调用析构函数,则行为未定义。示例:如果自动对象的析构函数被显式调用,并且块随后以通常会调用对象的隐式销毁的方式离开,则行为未定义。

此文本在我拥有的最新 C++11 草案(n3225,2010 年 11 月)中保持不变,并且在 2011 年 8 月批准之前,它不太可能在本质上发生变化。

【讨论】:

  • @AbhishekMane:当你(非放置)new 一个东西时,分配内存,然后调用构造函数,this 是该内存的地址。在delete 上,调用析构函数然后释放内存。实际的析构函数本身并不会为对象本身释放内存,但delete 会将其作为最后一步。
  • @AbhishekMane:这些确实应该是问题而不是 cmets,但是各种数据类别的“释放”方法是不同的。静态存储持续时间对象作为程序退出的一部分被释放,newed 对象由delete 释放,局部变量在其作用域结束时被释放。请注意,其中只有第二个涉及显式释放,其他由环境自动处理。
  • 例如,以下 sn-p 中的对象 xyzzy 将在右大括号处被破坏并“释放”:{myType xyzzy; xyzzy.someFunc(); } 请注意我在“释放”周围的引号,因为在这种情况下,术语不适用于正常的动态存储区域(有时称为空闲存储或堆)。
  • @paxdiablo 我终于明白了概念。基本上我在对象的破坏和内存存储的取消分配/破坏之间感到困惑。谢谢
  • @AbhishekMane,您可以访问从构造函数的左大括号一直到析构函数的右大括号的成员。在析构函数退出之前,不会释放内存。流程基本是allocate, construct, destruct, deallocate
【解决方案3】:

你所做的实际上是调用未定义的行为......仅仅因为你调用了析构函数,并不意味着内存被清零或必然“回收”并且不可访问(尤其是在自动变量的情况下)分配在堆栈上而不是堆上)。可能是这样,但这取决于实现,并且由于性能原因通常不会这样做,这通常是首先使用 C++ 的原因。因此,理论上您可以在调用析构函数后访问对象占用的内存地址处的值......但同样,这是未定义的行为,您可能会遇到几乎任何事情,从分段错误到破坏内存的静默错误其他地方等等。

【讨论】:

    【解决方案4】:

    它已执行,因为编写了表示您希望它发生的代码。编译器只是按照您的指示去做。

    您正在做的事情可能不会像您建议的那样“释放内存”。相反,它只是调用析构函数。析构函数不会释放被调用的对象占用的内存。它们释放对象分配的内存(例如通过调用成员变量的析构函数,或在其他事情上调用freedelete),但对象本身的内存在其他地方被释放,或者通过@ 的内部工作987654323@ 语句,或者由编译器在清理自动变量时(这是您的 B b 声明所代表的)。即使关闭范围块也可能不会为b 释放任何内存;编译器通常会计算出一个整个子例程需要多少堆栈空间,并在进入时将其全部分配。 B 对象占用的内存在进入内部作用域时为b 保留,退出时会自动调用析构函数。

    【讨论】:

    • 表示当object 的析构函数被调用时它被执行并且这个信号(为特定对象执行的析构函数)消失。然后是成员变量调用的析构函数(类似的东西),然后它取消分配内存。所以在执行析构函数后完成的所有事情都是内部工作的一部分。我对吗 ?或者我搞砸了
    【解决方案5】:

    您的对象已被销毁,但它的内存空间仍然存在,直到超出范围。

    【讨论】:

      【解决方案6】:

      为什么不呢?您的对象已被销毁,但它的内存空间仍然存在,直到它超出范围,顺便说一下,它将再次被销毁。做你所做的事情是未定义的行为。

      【讨论】:

        【解决方案7】:

        析构函数并不是为了显式调用它们而设计的。基本上它只是类的另一种(特殊)方法。 如果您想取消初始化您的对象,然后仍然可以使用它,您可以创建我们自己的方法:

        class B: public A
        {
        public:
         int b;
         B() {cout<<"b"<<endl;a=10;b=20;}
         ~B() {Release(); cout<<a<<b<<endl;}
         void Release() { cout<<"Releasing B"; b = 0; }
        };
        
        int main()
        {
            {
              B b;
              b.Release();
              b.b=100;  // why this step is executed?
            }
            int x;
            cin>>x;
            return 0;
        }
        

        否则B超出范围会被删除:

        int main()
        {
            {
              B b;
              b.b = 100;  //OK
            }
            b.b = 100; //compile time error
            int x;
            cin>>x;
            return 0;
        }
        

        【讨论】:

          猜你喜欢
          • 2018-03-10
          • 2011-01-25
          • 1970-01-01
          • 2022-01-23
          • 2010-10-16
          • 2021-08-03
          • 1970-01-01
          • 2020-02-29
          相关资源
          最近更新 更多