2010-10-27 59 views
6

我有这个简单的C++代码,但我不知道如何使用析构函数。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

很多答案引用“堆栈”。如果您不确定堆栈是什么,请阅读以下网页:http://web.archive.org/web/20071029040931/www.dirac.org/linux/gdb/02a-Memory_Layout_And_The_Stack.php – 2010-10-27 18:51:53

回答

1

你不应该明确地调用你的析构函数。

当你在栈上创建对象(像你这样),你需要的是:

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

(未按呼吁这最后的例子删除会导致记忆力减退。)

这两种方法都有各自的优点的d缺点。堆栈方式非常快,分配对象将占用的内存并且不需要显式删除它,但堆栈空间有限,无法轻松,快速和干净地移动这些对象。

堆是这样做的首选方式,但是当涉及到性能时,它分配缓慢,你必须处理指针。但是,对于对象的操作,你有更多的灵活性,进一步处理指针会更快,并且可以更好地控制对象的生命周期。

+2

“堆是做它的首选方式” - 做*什么*?存储对象?因为那会是错误的:堆栈绝对是在C++中执行该操作的首选方式。 – 2010-10-27 18:46:46

+0

使用库并在它们之间传递对象时,最好将它们分配到堆上(重要的)。确定堆栈是快速和肮脏的,并且在某些情况下是有意义的。但是当你想要使用对象时,传递,移动,复制和引用计数,那么堆栈就不适合他们。直到C++ 0x移动语义变得司空见惯,这是... – Marius 2010-10-27 19:01:38

+1

抱歉,但这是错误的。对于参数传递,使用(const)引用。对于返回值 - 只需复制。什么坏事都会发生。见例如http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/库不应该需要堆分配的参数。这将是一个**非常糟糕的库设计。关于堆栈分配没有什么快速和肮脏的。没关系; – 2010-10-27 19:20:11

9

您不需要明确调用析构函数。这是在对象ob2的范围的末尾,即在功能的末尾自动完成的。

此外,由于对象具有自动存储,所以它的存储不必删除。这也是在函数结束时自动完成的。

几乎从不需要手动调用析构函数(仅适用于低级库代码),并且只有在使用new(当您使用指针时才需要获取内存时才需要手动删除内存)。

由于手动内存管理容易泄漏,现代C++代码尝试不显式使用newdelete。当真的需要使用new时,使用所谓的“智能指针”而不是常规指针。

+0

这是很好的好吧,但我相信海报有一个更基本的问题。 – Marius 2010-10-27 19:07:39

+0

对第三段略作澄清:当使用新的位置时,显式调用析构函数是可以接受的(虽然很丑并且经常可以避免)。 – Sydius 2010-10-27 19:07:39

+0

@Sydius:这就是为什么我写“只在低级库代码”。我们不要通过抛出更高级的符号来进一步混淆OP。 – 2010-10-27 19:22:10

14

你很少需要明确调用析构函数。相反,析构函数在对象被销毁时被调用。

对于像ob2一个对象,它是一个局部变量,它是当它超出范围破坏:

int main() 
{ 
    date ob2(12); 

} // ob2.~date() is called here, automatically! 

如果动态地分配使用new的对象,当对象被销毁它的析构函数被调用使用delete。如果您有一个静态对象,则在程序终止时(如果程序正常终止),将调用其析构函数。除非您使用new动态创建某些内容,否则不需要进行任何明确的清理操作(例如,销毁ob2时,其所有成员变量(包括day)都将被销毁)。如果你动态地创建了一些东西,你需要确保它在完成时被破坏;最佳做法是使用所谓的“智能指针”来确保自动处理此清理。

0

在这种情况下,您的析构函数不需要删除day变量。

您只需要调用内存中已删除的新内容。

这里是你的代码是什么样子,如果你使用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; 
} 
+0

除了代码在堆上分配内存以存储整数之外,该样式还有什么问题? – 2010-10-27 18:38:05

+0

这就是我的意思。但是另外(正如我在下面发表的帖子中指出的那样),在现代C++中,任何与new指针结合使用的'new'都会被弃用,因为这会导致很多麻烦。当然适合初学者。 – 2010-10-27 18:43:13

+0

该代码还有一个问题:没有正确定义的复制构造函数和复制赋值运算符,这对初学者来说是一个非常常见的错误。 – 2010-10-27 18:49:05

2

。默认情况下,当您创建一个自动存储的变量并且超出范围时,或者当使用delete销毁与new动态分配的对象时,系统将调用析构函数。

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,这就是娱乐的原因的对象借助于的放置新

+0

分配内存的常用方式是使用malloc(或新的数组)。这是我用堆栈变量看到的第一个例子。顺便说一句,在第一个例子中,它会是test * d = new test(2); – 2010-10-27 18:44:25

+0

@VJo:我开始写一个带有堆栈分配内存缓冲区的例子,但是在调用析构函数之前需要使用placement new *(小问题),并且没有显示对析构函数进行多次调用的潜在问题。新标准中还有另一个* auto *分配的例子:对联合的成员放置较少的需求,它允许具有不平凡的构造/析构函数的对象。在这种情况下,用户可以显式调用联合活动成员的构造函数和析构函数。 – 2010-10-27 19:57:09

0

尽管析构函数看起来像是你需要调用的东西,当你完成使用它的时候,你不需要使用它,但是你不应该这样使用它。

析构函数是在对象超出范围时自动调用的,也就是当计算机离开实例化对象的“花括号”时。在这种情况下,当您离开main()时。你不想自己打电话。

0

您可能会对此处未定义的行为感到困惑。 C++标准没有关于如果在析构函数运行后使用对象会发生什么的规则,因为这是未定义的行为,因此实现可以做任何它喜欢的事情。通常情况下,编译器设计人员对未定义的行为不做任何特别的处理,所以会发生什么是其他设计决策的人为因素。 (这可能会导致真正奇怪的结果。)

因此,一旦运行析构函数,编译器就没有关于该对象的进一步义务。如果你不再提及它,那没关系。如果你真的提到它,那就是未定义的行为,从标准的角度来看,行为并不重要,而且由于标准没有说明什么,所以大多数编译器设计人员不会担心程序的功能。

在这种情况下,最简单的办法是不要触摸对象,因为它不占用资源,并且它的存储空间被分配作为启动该功能的一部分,并且直到该功能才会被回收退出。因此,数据成员的值将保持不变。当它读取ob2.day时,编译器自然要做的就是访问内存位置。

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