C ++中的析构函数(与java相比)

时间:2012-02-13 17:59:24

标签: java c++ garbage-collection

到目前为止,我一直在用Java编写程序。所以当我开始使用C ++时,首先想到的是如何破坏/删除/完成我不再需要的对象。

使用Java我曾经将它们设置为null所以垃圾收集器正在处理它。 但是,我不知道C ++的价值。我发现这篇文章http://en.wikipedia.org/wiki/Comparison_of_Java_and_C%2B%2B解决了我的大部分问题。但仍有一些我不明白的事情。

1)在Java中,有一种方法可以强制垃圾收集器在现场清理(这并不总是有用,因为它在运行之前等待一些垃圾堆叠起来)。有没有办法用C ++做到这一点?

2)(C ++)也与上述相反,我怎样才能使对象处于“标记为删除”的状态,并且程序决定何时清理它(如Java)? / p>

3)(C ++)我应该迫使垃圾收集器在现场清理(我很确定这不是正确的方法,但我要求确定)?

如果你能给出一个代码触发器的小代码示例,我会赞美它。

5 个答案:

答案 0 :(得分:6)

1)如果您的对象处于自动存储中,您可以限制其范围:

{
   X x;
   //...
}  //x gets destructed here

如果在动态存储中,则在完成后删除它们:

X* x = new X;
//...
delete x; //x gets destructed

2)你不能(至少以干净的方式)。您必须指示C ++何时删除对象,即使该指令包含结束括号。 (请参阅第一个代码段)

3)C ++中没有垃圾收集器。看两个片段。您要么必须显式删除对象(如果在动态存储中),要么在自动存储中自动删除它们(但不是垃圾收集器)。

值得研究的是智能指针(那里有大量的实现),但这也不是垃圾收集器。它只是为您节省了管理内存的麻烦。但它与Java无关。

答案 1 :(得分:4)

C ++在这方面与Java非常不同,所以这里有一个简要概述:

分配:为对象留出内存 结构:准备使用该物体。
破坏:物体“完成”一切并自行拆解。
解除分配:将内存返回给系统。

int main() {
    int myint;  //automatic int object is allocated and constructed
    //stuff
}   // when main ends, automatic int object is destroyed and deallocated

int main() {
    int* mypointer;  //automatic pointer object is allocated and constructed
    mypointer = new int;  //dynamic int object is allocated and constructed
    //stuff
    delete mypointer; //dynamic int object is destroyed and deallocated 
}   // when main ends, automatic pointer object is destroyed and deallocated
    // note: Pointers to _not_ delete the object they point to.

class myclass {
    //members
public:
    myclass() {} //this is the default constructor
    myclass(const myclass& rhs) {} //this is the copy constructor
    myclass& operator=(const myclass& rhs) {return *this} //this is the assignment operator
    ~myclass() {} //this is the destructor
};

当一个函数结束时,函数本身的所有变量(我们称之为自动变量)都会调用它们的析构函数,然后自动释放它们。这意味着对于函数本地的对象,它们会自动清除函数结束的 instant 。这也适用于一个类的成员。当它被销毁时,它的每个成员都将被自动销毁。这意味着大多数析构函数都是空的。

如果手动分配内容(使用new关键字),则必须使用delete关键字手动销毁和取消分配内容。当你调用delete时,它会在那里销毁(并解除分配)然后,并且在完成之前不会继续。如果您忘记了,它将无法获得DEALLOCATED(尽管某些操作系统将在您的程序结束时解除分配)。

由于人们会犯错误,因此在需要动态对象时要做的“正确”事情是:

int main() {
    std::unique_ptr<myclass> myptr = new myclass(); //allocate and construct
} //both the unique_ptr and the dynamic object are destroyed and deallocated

并且unique_ptr非常聪明,能够自动清理它指向的内容,让您免于更大的顾虑。

C ++执行此操作的原因是因为如果您有一个表示该文件的对象F,它可能对该文件具有独占锁定。在C ++中,一旦F被销毁,您就可以立即创建使用该文件的对象G。在Java中,无法保证finalizer永远运行,这意味着文件可能会在程序结束前保持锁定状态。 (不太可能,但可能)

答案 2 :(得分:3)

C ++中没有垃圾收集器。你应该自己编写和运行析构函数。在C ++中,忘记运行析构函数是一个常见的错误。

如果您的对象已分配new,则应使用delete将其删除。因此,new调用contructor,而delete调用析构函数。

myclass *p = new myclass();
// do something
delete p;

这称为动态对象分配。

如果您的对象是“正常”定义的,当超出范围时它将自动被破坏。

myclass a;
// do something
// will destructed when }

这称为自动对象分配。

P.S。您也没有在Java中分配空值,因为垃圾收集器是为了不打扰删除对象而发明的。

答案 3 :(得分:1)

C ++中的垃圾收集始终是即时的。没有单独的垃圾收集器;删除对象时,它会立即删除当前线程。它看起来像这样:

MyObject* foo = new MyObject();
...
delete foo;

有可用于C ++的垃圾收集框架,您还可以查看智能指针,这也是垃圾收集的一种形式。

注意下面的James评论 - 对象的析构函数和操作符删除总是立即被调用,但是它是依赖于实现的,关于内存是否可以立即可用。

答案 4 :(得分:1)

C ++使用RAII(资源获取是初始化)编程习惯,没有什么比在Java中称为垃圾收集器或在Objective-C 2中称为AutoZone的自动内存管理。因此,正确的实例清理很容易变得复杂。回答你的问题:

广告1:C ++中没有GC,因此您必须手动删除对象或使用引用计数技术或更好的智能指针,它们现在是C ++ 11标准的一部分,但据我所知,它是尚未在任何C ++编译器中提供。现在,您可以使用Boost库中的智能指针模板:http://www.boost.org/doc/libs/1_48_0/libs/smart_ptr/smart_ptr.htm。新的C ++标准直接采用了Boost实现,因此在不久的将来切换到新标准时将没有问题(MSVC 2012将实现C ++ 11支持)。

ad 2:无法标记,只需在右侧“手动”删除它,或将此任务保留在智能指针上。

广告3:不适用。

最后,始终有最简单的选项 - 不要在堆上分配对象,这意味着动态。在Java中没有这种可能性,但在C ++中有。 我甚至读过一些关于C ++编程的Stroustrup(C ++的创建者)的好书,在C ++创建时,不推荐这样的动态分配。他表示:要让RAII正常工作,一定不能进行动态分配 - 今天听起来很奇怪,但这就是Stroustrup所写的内容,它不是我的头脑,我个人动态分配几乎所有人都做的事情......

静态分配的主要原因是一旦超出范围就会删除对象,因此根本不必担心异常安全和清理。如果您动态分配实例,则如果实例离开当前范围,则不会自动删除它 - 如果您没有手动删除实例,则会发生内存泄漏。考虑一下简单的try-catch块:

try 
{
Class *instance = new Class;
//some error
}
catch(...)
{
//error caught - current execution is terminated immediately, instance is no deleted - memory leak.
}

我在Java中有一个始终被调用的finally语句,因此您可以在抛出异常时执行必要的清理。但是在C ++中你遇到麻烦... 除非你使用提到的智能指针或一些非常类似的技术。当使用智能指针时你不必再担心清理了(在实践中并不完全正确) ,但你的生活肯定会更容易,你的代码也会减少错误。)