什么是在c ++中释放内存的最佳方式 [英] what is the best way of deallocating memory in c++

查看:184
本文介绍了什么是在c ++中释放内存的最佳方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在C ++中使用类和嵌套类。我有很多类和嵌套类。我不知道什么时候应该释放内存。有一个软件,显示你在哪里或何时从一个类释放指针?

I am working in C++ with classes and nested classes. I do have a lot of classes and nested classes. I don't know when should i deallocate the memory. Is there a software that shows you where or when to deallocate a pointer from a class?

任何人都可以帮助我吗?
thx

Can anyone help me? thx

我想说的是我在ubuntu下使用c ++。我的文件是.cpp和.h文件。没有主。
我使用class * x = new class()。我没有删除x,因为我不知道我应该何时删除它。

I would like to mention that i am working in c++ under ubuntu. My files are a .cpp and a .h file. There is no main. I am using class *x= new class(). I didn't did delete x because I don;t know when should i delete it.

推荐答案

表示一些关于C ++如何工作的混乱。有一些重要的事情你需要了解一些事情:

To the OP: your comments indicate some confusion as to how C++ works. There are some important things you need to understand a few things:


  1. 处理指针本质上是危险的。不正确地使用指针可能会引入未定义的行为,这不仅是您的程序中的错误,但它是一个错误,可能不会被编译器检测,甚至不给你一个wanring,在运行时没有告诉你的程序将如何行为:它可能会崩溃,然后,但它也可能继续运行,但导致程序的另一部分,这是100%正确,要么崩溃,或者只是表现不同于它应该。没有自动的方法来确定地检测这些情况,所以良好的设计是至关重要的。

  2. 指向对象的指针与它指向的对象不同。作为附注,如果你来自一种类似Java的语言,在C ++中,术语object也指内置类型的变量,如int,double等。

  3. 到类型X是一个值类型,可能或可能不指向一个对象来键入X.除了有一个特殊的值0(NULL),它可以简单地指向一个随机的地方在内存中不包含一个对象类型X。这样的指针被称为悬挂指针。

  4. 访问指针指向的对象的动作称为解引用指针。引用悬空指针或空指针是未定义的行为。此外,虽然你的程序可能检查指针是否为null,但是不可能检查它是否是一个悬空指针。程序员有责任以这种方式构造程序,这样悬挂指针就不会被取消引用。

  5. 在您的一个问题中,您需要创建一个指针。创建指针绝对不意味着你必须调用 delete ,因为创建指针不同于创建指针指向的对象。考虑下面的代码段,它创建了一个指针: SomeType * pointer;

    虽然这实际上创建了一个指针,它是一个悬空指针,如果你尝试调用 delete ,你将得到未定义的行为。

  6. 内存分配的问题不是具有指针,如同对于其分配或解除分配存储器的对象一样。通常,你不能在C ++中显式地分配或释放内存,而是创建和销毁对象,并为它们隐式地分配内存。

  7. new 操作符不创建指针,它创建动态对象,为它们分配所需的内存,并返回一个指向新创建的对象的指针。动态分配的对象的生命周期将持续存在,并且它们将继续占据它们所占用的内存,直到您在指针上调用 delete ,这会销毁动态对象(而不是指针本身)释放它占用的内存。如果你重复分配动态对象,并且在停止使用动态对象后无法删除动态对象,则内存将被未使用的对象填充:这称为内存泄漏。

  8. 指针不仅指向动态分配的对象,它们也可以指向其他类型的对象:局部变量,结构或类成员,数组元素。正如没有办法告诉一个指针是否悬空,也没有办法告诉它是否指向一个动态对象。尝试在指向动态对象以外的有效指针上调用 delete 会导致未定义行为。

  9. 可能有许多指向同一对象的指针。如果对象被销毁,通过在动态对象的情况下调用delete,或以任何其他方式,所有指向它的指针都会变成悬空指针。

  10. 指针变量或类/ struct成员是它们自己的对象,但是它们的生命周期不受它们指向的任何对象的限制:正如在创建指针时没有创建指向对象一样,指向的对象(如果有的话)不会被销毁指针被销毁。这可以安全地销毁指向同一个对象的多个指针,也可以销毁悬挂指针。

  1. Dealing with pointers is inherently dangerous. Incorrectly using pointers may introduce Undefined Behaviour, which not only is an error in your program, but it's an error that may well not be detected by the compiler, not even giving you a wanring, and at runtime there is no telling how your program will behave: it may crash right there and then, but it may also continue running, but cause another part of the program, which is 100% correct, to either crash or just behave differently than it should. There is no automatic way to detect these situations with certainty, so good design is paramount.
  2. A pointer to an object is a different thing than the object it points to. As a side note, if you come from a language like Java, in C++ the term 'object' also refers to variables of built-in types, like int, double etc.
  3. A pointer to type X is a value type, that may or may not point to an object to type X. Apart from having a special value of 0 (NULL), it may simply point to a random place in memory which does not contain an object of type X. Such a pointer is called a 'dangling pointer'.
  4. The act of accessing an object pointed to by a pointer is called dereferencing the pointer. Dereferencing a dangling pointer or a null pointer is Undefined Behaviour. Furthermore, while it is possible for your program to check if a pointer is null, it's not possible to check if it's a dangling pointer. It is the programmer's responsibility to structure the program in such a way, that dangling pointers are never dereferenced.
  5. In one of your questions, you ask about creating a pointer. Creating a pointer definitely does not mean you have to call delete on it, because creating a pointer is different to creating an object to which the pointer would point. Consider the following piece of code, which creates a pointer: SomeType* pointer;
    While this does in fact create a pointer, it is a dangling pointer, and if you attempt to call delete on it, you will get Undefined Behaviour.
  6. The question of memory allocation is not to do with pointers as such, as with objects, for which memory is allocated or de-allocated. Typically, you do not allocate or de-allocate memory explicitly in C++, but instead create and destroy objects, and memory is allocated for them implicitly.
  7. The new operator does not create pointers, it creates dynamic objects, allocating the required memory for them, and returns a pointer to the newly created object. Dynamically allocated objects' lifetime will persist and they will keep occupying the memory that they occupy until you call delete on the pointer, which destroys the dynamic object (not the pointer itself) and deallocates the memory it occupies. If you repeatedly allocate dynamic object and fail to delete them after you stop using them, your memory gets filled up with unused objects: this is called a memory leak.
  8. Pointers may point not just to dynamically allocated objects, they may point to other kinds of objects as well: local variables, structure or class members, array elements. Just as there is no way of telling if a pointer is dangling, there is also no way to tell if it points to a dynamic object. Attempting to call delete on a valid pointer that points to anything else than a dynamic object results in Undefined Behaviour.
  9. There may be many pointers pointing at the same object. If the object is destroyed, either by calling delete in the case of dynamic object, or in any other way, all pointers, that pointed to it, become dangling pointers.
  10. Pointer variables or class/struct members are objects in their own right, but their lifetimes are not tied to whatever objects they point to: just as no pointed-to object is created when you create a pointer, the object pointed to (if any) is not destroyed when the pointer is destroyed. This makes it possible to safely destroy multiple pointers pointing to the same object, or to destroy dangling pointers.

创建动态使用 new 的对象并使用 delete 销毁它们是异常安全:在许多情况下, code> delete 使用 new 创建的所有内容可能不会这样做,因为会抛出异常。例如:

An additional concern when creating dynamic objects with new and destroying them with delete is exception safety: in many cases code that looks at first like it would delete everything created with a new might not do so, because an exception is thrown. For example:

{
    SomeType *p = new Sometype
    // some code
    delete p;
}

在这种情况下,如果'some code'抛出异常, delete p 永远不会到达,并且发生内存泄漏。 Silimlarly:

In this case if 'some code' throws an exception, delete p is never reached and a memory leak occurs. Silimlarly:

class A {
    SomeType *p;
    // more members
public:
    A(): p(new SomeType()) /* more intializers */ { /* body */ }
    ~A() { delete p; }
};

在这种情况下,如果构造函数在p初始化后抛出异常,析构函数将永远不会被调用

In this case, if A constructor throws an exception after p is initialized, the destructor will never get called and you will have a memory leak.

鉴于这些缺陷,存在几种设计技术来管理这些问题。

Given these pitfalls, several design techniques exist to manage these problems.

除非必要,否则不要使用指针和/或动态对象:在许多情况下,您可以在不动态分配对象的情况下获取。在局部变量的情况下,其中变量的生命期限于函数范围,您只需将对象定义为自动变量,因此代替:

Don't use pointers and/or dynamic objects unless necessary: In many cases, you can get by without dynamically allocating objects. In the case of local variables, where the variable's lifetime is limited to function scope you can just define the object as an automatic variable, so instead of:

{
    SomeType* p = new SomeType();
    // do something with *p
    delete p;
}

您可以:

{
    SomeType var;
    // do something with var
} // var destroyed here

第二种情况,var将被销毁,并且当它超出范围时,即使抛出异常,它的内存也将被释放。

In the second case, var will be destroyed and it's memory will be deallocated when it goes out of scope - even if an exception is thrown.

同样,对于包含其他对象的对象,您可以直接包含对象作为成员,所以代替:

Similarly, for objects containing other objects, you can include the object directly as a member, so instead of:

class Containing {
    Contained* member;
};

您可以执行

class Containing {
    Contained member;
};

在这种情况下,包含成员的内存在包含对象内,并且当包含对象被释放,另外,如果包含的成员在包含对象的构造期间被初始化,但是包含的构造器然后抛出异常,则保证包含的成员将被正确地销毁,包括它自己的析构函数被调用,即使包含对象析构函数不被调用。

In this case the memory for contained member is inside the containing object and will be deallocated when the containing object is deallocated, additionally, if the contained member was initialized during the containing object's construction, but the containing constructor then threw an exception, it is guaranteed that the contained member will be properly destroyed, including its own destructor being called, even though the containing object's destructor isn't called.

由于没有指针涉及到这里,你不必担心悬挂或空指针,你得到额外的优势减少时间和内存开销。

As there are no pointers involved in this, you don't have to worry about dangling or null pointers, and you get the additional advantage of reduced time and memory overhead.

缺点是:数据结构中没有递归(所以对于列表,树等,你需要使用指针),没有多态到SomeType可能指向SomeType的对象或从SomeType派生的类型,SomeType类型的自动变量只能是该特定类型),并且没有延迟初始化(这对于类数据成员尤其重要,其构造函数参数必须始终

The disadvantages are: no recursion in data structures (so for lists, trees etc. you need to use pointers), no polymorphism (a pointer to SomeType may point to an object of SomeType or a type derived from SomeType, an automatic variable of type SomeType can only be of that specific type), and no delayed initialization (this is especially important for class data memebers, whose constructor parameters must always be given in the containing class initializer list, while a pointer can be (re)assigned at any time.

在必须使用动态对象的情况下,您需要依赖于设计以确保您的动态对象被删除。典型的模式是拥有者的概念 - 一个负责删除动态对象的实体。在这个概念中,每个动态对象都有一个且只有一个所有者,它是当前正在执行的函数或保存指向所拥有对象的指针的另一个对象。因此,可能有许多指向一个对象的指针,但只有一个是拥有指针。这在许多情况下适用:创建一个动态对象的函数,只要运行的函数只有函数运行将拥有该对象,这意味着必须在函数本身的代码执行删除。在任何类型的分层数据结构(如树)中,父对象可以拥有其孩子。一些动态数据结构(如链表)可以包含在拥有其动态节点的对象中,并且负责创建和删除它们,在此示例中,当向列表中添加或删除元素时。

Where dynamic objects must be used, you need to rely on design to make sure your dynamic objects are deleted. The typical pattern for this is the concept of an 'owner' - being an entity responsible for deleting dynamic objects. In this concept, every dynamic object has one and only one 'owner', which is a currently executing function or another object that holds a pointer to the owned object. Thus there may be many pointers to one object, but only one is the owning pointer. This is applicable in many cases: a function that creates a dynamic object that lives only as long as the function runs will own that object, meaning there must be code in the function itself that does the delete. In any type of hierarchical data structures (like trees) a 'parent' object can own its 'children'. Some dynamic data structures, like linked lists, can be wrapped in objects that own their dynamic nodes and are responsible for both creating and deleting them, in this example when an element is added to or removed from the list.

在更复杂的情况下,所有权可以在不同的代码段之间传输。例如,动态对象的集合可以假设在它们之外创建的对象的所有权,当它们被添加 insert 方法,并且放弃拥有使用get-and-remove语义(pop,dequeue ...)提取方法。创建动态对象和返回指针的函数可以将所有权传递给调用者,等等。

In more complex cases ownership can be transferred between various pieces of code. For example a collection of dynamic objects may assume ownership of objects created outside of it when they are added with an insert method, and relinquish that ownership with an extract method with get-and-remove semantics (pop, dequeue...). A function which creates dynamic objects and return pointers to them may pass ownership to the caller, and so on.

对于保留动态对象所有权的对象,确保它们在析构器中被删除,因为如果它不发生,当所有者被销毁时,所拥有的对象将保持没有所有者,意味着它将永远不会被删除,有效地成为内存泄漏。所有者也可以在它被销毁之前删除所拥有的对象,这在许多情况下是有用的。一个警告:确保所有者没有留下一个悬挂指针,它会考虑指向一个有效的对象,并尝试再次销毁。在某些情况下,当你用一个新的对象替换它时,你会销毁一个拥有的对象,在这种情况下你没有这个问题,因为拥有指针现在将指向新的拥有对象,这也是有效的。在其他情况下,你将希望将拥有指针设置为0,因此对象知道它不再拥有任何在该指针下的任何东西(在空指针上调用delete也是安全的)。

With objects maintaining ownership of dynamic objects, you will always want to ensure they are deleted in the destructor, because if it doesn't happen, when the owner gets destroyed, the object owned will remain without an owner, meaning it will never be deleted, effectively becoming a memory leak. The owner can also delete the owned object before it is destroyed itself, which is useful in many circumstances. One caveat though: make sure that the owner is not left with a dangling pointer that it would consider to be pointing at a valid object and try to destroy again. In some cases, you destroy an owned object when you're replacing it with a new one, in which case you don't have this problem, as the owning pointer will now point to the new owned object, which is also valid. In other cases you will want to set the owning pointer to 0, so the object know it does not own anything under that pointer any more (it is also safe to call delete on null pointer).

使用手动 delete 在函数体或析构函数中维护所有权有几个问题:首先,这个所有权不在代码中表示,你必须单独管理它,这是容易出错的(因为编译器将无法检测到任何错误)。这是由于我上面提到的异常问题进一步复杂:它是困难,并再次,容易错误,编写代码,以便删除将被调用,无论在哪里抛出一个异常。

There are a couple of problems with using manual deletes in function body or destructors to maintain ownership: first, this ownership is not expressed in code, you have to manage it separately, which is error-prone (as the compiler will and cannot detect any mistakes here). This is further complicated by the exception issue I mentioned above: it is hard, and again, susceptible to errors, to write code so that delete will be called no matter where an exception is thrown.

在其他答案中提到的boost和标准库中的智能指针帮助以异常安全的方式明确表达所有权语义。 STL的 auto_ptr 和boost scoped_ptr 具有上述严格的所有权语义,分别具有和不具有转让所有权的能力。如果你幸运地使用当前标准C ++,unique_ptr是auto_ptr的替代品,它消除了它的一些问题。新标准STL,boost和TR1的 shared_ptr 具有共享所有权语义,其中对象具有多个所有者,并在最后一个所有者允许时被销毁。使用每种智能指针类型都有其优点和注意事项,您应在使用它们之前仔细阅读。

Smart pointers in boost and standard library, mentioned in other answers, help explicitly express ownership semantics in an exception-safe manner. STL's auto_ptr and boost scoped_ptr have strict ownership semantics described above, respectively with and without the ability to transfer ownership. If you are fortunate enough to use the current standard C++, unique_ptr is a replacement for auto_ptr that does away with some of its problems. The shared_ptr of new standard STL, boost and TR1 has 'shared ownership' semantics, where an object has several 'owners' and is destroyed when the last owner lets go of it. There are advantages and caveats to using each of these smart pointer types, which you should read up on before using them.

这篇关于什么是在c ++中释放内存的最佳方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆