我正在调用析构函数来释放内存,但它并没有删除我的对象。它背后的原因是什么?
我的代码是这样的:
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;
}
答案 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)
已执行,因为您编写了表示您希望它发生的代码。编译器只是做你告诉它的事情。
你正在做什么可能“释放内存”,正如你所建议的那样。相反,它只是调用析构函数。析构函数不会释放它们被调用的对象占用的内存。它们释放由对象分配的内存(例如通过调用成员变量的析构函数,或者在其他事物上调用free
或delete
),但是对象本身的内存在其他地方被释放,或者由内部释放清除自动变量时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;
}