如何调用类的析构函数?

时间:2010-10-27 18:22:06

标签: c++ destructor

我有一个简单的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变量

7 个答案:

答案 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 ++代码尝试根本不使用newdelete。当真正需要使用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时要做的一件事就是访问内存位置。

与任何其他未定义行为的示例一样,结果可能会在情况发生任何变化时发生变化,但在这种情况下,它们可能不会。如果编译器能够捕获更多未定义行为的情况并发出诊断信息,那将是很好的,但是编译器无法检测所有未定义的行为(有些在运行时发生)并且通常不会检查他们不认为的行为可能的。