显式调用析构函数是不是要破坏我的对象为什么?

时间:2011-09-13 02:04:32

标签: c++ destructor

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

我的代码是这样的:

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;
}

7 个答案:

答案 0 :(得分:19)

  

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

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

非平凡的析构函数会终止对象的生存期,但它不会终止对象的存储持续时间。这意味着内存仍然被分配,它只是变得“原始”(未初始化)。 所以,从这个意义上说,它是摧毁你的对象。

与此同时,一个简单的析构函数根本没有效果。即使你明确地调用它,对象的生命周期也不会结束。

在你的情况下,析构函数B::~B虽然是非平凡的,但正式意味着通过调用它来结束对象的生命周期。你破坏了它,因为可以销毁一个本地对象。 But the memory remains。尝试将该内存作为B对象进行访问只会导致未定义的行为。

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

答案 1 :(得分:5)

调用这样的析构函数(好吧,你可以但通常没有完成)。

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

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

C ++ 03在12.4 Destructors中声明:

  

隐式调用析构函数:

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

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

     

注意:很少需要显式调用析构函数。此类调用的一个用途是使用带有placement选项的new-expression放置在特定地址的对象。为了应对专用硬件资源和编写内存管理设施,可能需要使用显式放置和销毁对象。

特别是没有做你想要做的事情,因为析构函数将被调用两次,一次由你明确调用,一次隐式地调用b超出范围。从标准的同一部分:

  

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

这篇文章在我最近的C ++ 11草案(n3225,2010年11月)中保持不变,并且它本质上不太可能在2011年8月的批准和批准之间发生变化。

答案 2 :(得分:4)

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

答案 3 :(得分:3)

已执行,因为编写了表示您希望它发生的代码。编译器只是做你告诉它的事情。

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

答案 4 :(得分:1)

您的对象已被破坏,但是它的内存空间仍然存在,直到超出范围为止。

答案 5 :(得分:0)

为什么不呢?你的对象已被破坏,但它的内存空间仍然存在,直到它超出范围,在那里它将被再次销毁。做你做的事情是不明确的行为。

答案 6 :(得分:0)

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

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;
}