我有一个简单的C ++代码,但我不知道如何使用析构函数:
class date {
public:
int day;
date(int m)
{
day =m;
}
~date(){
cout << "I wish you have entered the year \n" << day;
}
};
int main()
{
date ob2(12);
ob2.~date();
cout << ob2.day;
return 0;
}
我的问题是,我应该在析构函数代码中写什么,在调用析构函数之后,它会删除day
变量?
答案 0 :(得分:16)
你很少需要明确地调用析构函数。相反,当一个对象被销毁时,就会调用析构函数。
对于像ob2
这样的局部变量这样的对象,当它超出范围时会被销毁:
int main()
{
date ob2(12);
} // ob2.~date() is called here, automatically!
如果使用new
动态分配对象,则在使用delete
销毁对象时会调用其析构函数。如果你有一个静态对象,它会在程序终止时调用它的析构函数(如果程序正常终止)。
除非使用new
动态创建内容,否则不需要做任何明确的事情来清理它(例如,当ob2
被销毁时,它的所有成员变量,包括day
,被摧毁)。如果你动态创建一些东西,你需要确保它在完成后被破坏;最好的做法是使用所谓的“智能指针”来确保自动处理这种清理。
答案 1 :(得分:11)
您不需要显式调用析构函数。这是在对象ob2
范围的末尾自动完成的,即在main
函数的末尾。
此外,由于对象具有自动存储功能,因此不必删除其存储空间。这也是在函数结束时自动完成的。
几乎不需要手动调用析构函数(仅限于低级库代码),并且只有在使用new
先前获取内存时才需要手动删除内存(并且只需要有效操作)(当您使用时)使用指针)。
由于手动内存管理容易出现泄漏,现代C ++代码尝试根本不使用new
和delete
。当真正需要使用new
时,则使用所谓的“智能指针”而不是常规指针。
答案 2 :(得分:4)
你不应该明确地调用你的析构函数。
当您在堆栈上创建对象时(就像您一样),您只需要:
int main()
{
date ob2(12);
// ob2.day holds 12
return 0; // ob2's destructor will get called here, after which it's memory is freed
}
当您在堆上创建对象时,在调用析构函数并释放内存之前,您需要delete
您的类:
int main()
{
date* ob2 = new date(12);
// ob2->day holds 12
delete ob2; // ob2's destructor will get called here, after which it's memory is freed
return 0; // ob2 is invalid at this point.
}
(在最后一个示例中未能调用delete将导致内存丢失。)
两种方式各有利弊。堆栈方式非常快,分配对象将占用的内存,您不需要显式删除它,但堆栈空间有限,您无法轻松,快速,干净地移动这些对象。
堆是首选的方法,但是当涉及到性能时,分配速度很慢,你必须处理指针。但是你对对象的操作有了更大的灵活性,使用指针的速度更快,你可以更好地控制对象的生命周期。
答案 3 :(得分:3)
只有在非常特殊的情况下才需要直接调用析构函数。默认情况下,当您创建自动存储变量并且它超出范围或者使用new
销毁使用delete
动态分配的对象时,系统将调用析构函数。
struct test {
test( int value ) : value( value ) {}
~test() { std::cout << "~test: " << value << std::endl; }
int value;
};
int main()
{
test t(1);
test *d = new t(2);
delete d; // prints: ~test: 2
} // prints: ~test: 1 (t falls out of scope)
为了完整性,(通常不应该使用)调用析构函数的语法类似于方法。运行析构函数后,内存不再是该类型的对象(应作为原始内存处理):
int main()
{
test t( 1 );
t.~test(); // prints: ~test: 1
// after this instruction 't' is no longer a 'test' object
new (&t) test(2); // recreate a new test object in place
} // test falls out of scope, prints: ~test: 2
注意:在t
上调用析构函数后,该内存位置不再是test
,这就是重新创建对象的原因< em> placement new 。
答案 4 :(得分:0)
在这种情况下,析构函数不需要删除day变量。
您只需要在已使用new分配的内存上调用delete。
以下是使用new和delete来触发调用析构函数时代码的外观
class date {
public: int* day;
date(int m) {
day = new int;
*day = m;
}
~date(){
delete day;
cout << "now the destructor get's called explicitly";
}
};
int main() {
date *ob2 = new date(12);
delete ob2;
return 0;
}
答案 5 :(得分:0)
即使析构函数看起来像你需要调用以在你使用它时删除或“破坏”你的对象,你也不应该那样使用它。
析构函数是当对象超出范围时自动调用的东西,也就是说,当计算机离开你实例化对象的“花括号”时。在这种情况下,当你离开main()时。你不想自己打电话。
答案 6 :(得分:0)
您可能会对此处未定义的行为感到困惑。如果你在运行析构函数之后使用一个对象,那么C ++标准没有规则,因为这是未定义的行为,因此实现可以做任何它喜欢的事情。通常,编译器设计者不会对未定义的行为执行任何特殊操作,因此会发生什么是其他设计决策的工件。 (这有时会导致非常奇怪的结果。)
因此,一旦运行了析构函数,编译器就没有关于该对象的进一步义务。如果你再没有提到它,那没关系。如果您确实引用它,那是未定义的行为,从标准的角度来看,行为无关紧要,并且由于标准没有说明,大多数编译器设计者都不会担心程序的作用。
在这种情况下,最简单的方法是保持对象不变,因为它没有保留资源,并且它的存储是作为启动函数的一部分分配的,并且在函数退出之前不会被回收。因此,数据成员的值将保持不变。编译器在读取ob2.day
时要做的一件事就是访问内存位置。
与任何其他未定义行为的示例一样,结果可能会在情况发生任何变化时发生变化,但在这种情况下,它们可能不会。如果编译器能够捕获更多未定义行为的情况并发出诊断信息,那将是很好的,但是编译器无法检测所有未定义的行为(有些在运行时发生)并且通常不会检查他们不认为的行为可能的。