C++ 为什么使用'new'会导致内存泄漏?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8839943/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 19:11:54  来源:igfitidea点击:

Why does the use of 'new' cause memory leaks?

c++pointersmemory-leaksnew-operatorc++-faq

提问by R. Martinho Fernandes

I learned C# first, and now I'm starting with C++. As I understand, operator newin C++ is not similar to the one in C#.

我首先学习了 C#,现在我开始学习 C++。据我了解,newC++ 中的运算符与 C# 中的运算符不同。

Can you explain the reason of the memory leak in this sample code?

你能解释一下这个示例代码中内存泄漏的原因吗?

class A { ... };
struct B { ... };

A *object1 = new A();
B object2 = *(new B());

回答by R. Martinho Fernandes

What is happening

怎么了

When you write T t;you're creating an object of type Twith automatic storage duration. It will get cleaned up automatically when it goes out of scope.

当您编写时,T t;您正在创建一个T具有自动存储持续时间类型的对象。当它超出范围时,它将被自动清理。

When you write new T()you're creating an object of type Twith dynamic storage duration. It won't get cleaned up automatically.

当您编写时,new T()您正在创建一个类型T动态存储持续时间的对象。它不会被自动清理。

new without cleanup

新的没有清理

You need to pass a pointer to it to deletein order to clean it up:

您需要将指向它的指针传递给以delete清理它:

newing with delete

删除新

However, your second example is worse: you're dereferencing the pointer, and making a copy of the object. This way you lose the pointer to the object created with new, so you can never delete it even if you wanted!

但是,您的第二个示例更糟:您正在取消引用指针,并制作对象的副本。这样你就失去了指向用创建的对象的指针new,所以即使你想你也永远不能删除它!

newing with deref

使用 deref 更新

What you should do

你应该做什么

You should prefer automatic storage duration. Need a new object, just write:

您应该更喜欢自动存储持续时间。需要一个新对象,只需写:

A a; // a new object of type A
B b; // a new object of type B

If you do need dynamic storage duration, store the pointer to the allocated object in an automatic storage duration object that deletes it automatically.

如果确实需要动态存储期,请将指向已分配对象的指针存储在自动删除它的自动存储期对象中。

template <typename T>
class automatic_pointer {
public:
    automatic_pointer(T* pointer) : pointer(pointer) {}

    // destructor: gets called upon cleanup
    // in this case, we want to use delete
    ~automatic_pointer() { delete pointer; }

    // emulate pointers!
    // with this we can write *p
    T& operator*() const { return *pointer; }
    // and with this we can write p->f()
    T* operator->() const { return pointer; }

private:
    T* pointer;

    // for this example, I'll just forbid copies
    // a smarter class could deal with this some other way
    automatic_pointer(automatic_pointer const&);
    automatic_pointer& operator=(automatic_pointer const&);
};

automatic_pointer<A> a(new A()); // acts like a pointer, but deletes automatically
automatic_pointer<B> b(new B()); // acts like a pointer, but deletes automatically

newing with automatic_pointer

使用automatic_pointer 更新

This is a common idiom that goes by the not-very-descriptive name RAII (Resource Acquisition Is Initialization). When you acquire a resource that needs cleanup, you stick it in an object of automatic storage duration so you don't need to worry about cleaning it up. This applies to any resource, be it memory, open files, network connections, or whatever you fancy.

这是一个常见的成语,其名称不是非常具有描述性,RAII(Resource Acquisition Is Initialization)。当您获得需要清理的资源时,您将其粘贴在一个自动存储持续时间的对象中,因此您无需担心清理它。这适用于任何资源,无论是内存、打开的文件、网络连接还是您喜欢的任何资源。

This automatic_pointerthing already exists in various forms, I've just provided it to give an example. A very similar class exists in the standard library called std::unique_ptr.

这个automatic_pointer东西已经有各种形式存在了,我只是举个例子。标准库中存在一个非常相似的类,称为std::unique_ptr.

There's also an old one (pre-C++11) named auto_ptrbut it's now deprecated because it has a strange copying behaviour.

还有一个旧的(C++11 之前的)命名,auto_ptr但它现在已被弃用,因为它具有奇怪的复制行为。

And then there are some even smarter examples, like std::shared_ptr, that allows multiple pointers to the same object and only cleans it up when the last pointer is destroyed.

然后还有一些更聪明的例子,比如std::shared_ptr,允许多个指针指向同一个对象,并且只有在最后一个指针被销毁时才清理它。

回答by Luchian Grigore

A step by step explanation:

一步一步的解释:

// creates a new object on the heap:
new B()
// dereferences the object
*(new B())
// calls the copy constructor of B on the object
B object2 = *(new B());

So by the end of this, you have an object on the heap with no pointer to it, so it's impossible to delete.

所以到最后,你在堆上有一个没有指向它的对象,所以不可能删除。

The other sample:

另一个样本:

A *object1 = new A();

is a memory leak only if you forget to deletethe allocated memory:

仅当您忘记delete分配的内存时才会发生内存泄漏:

delete object1;

In C++ there are objects with automatic storage, those created on the stack, which are automatically disposed of, and objects with dynamic storage, on the heap, which you allocate with newand are required to free yourself with delete. (this is all roughly put)

在 C++ 中有具有自动存储的对象,在堆栈​​上创建的对象会自动处理,而具有动态存储的对象在堆上,您可以使用这些对象进行分配,new并且需要使用delete. (这都大致放了)

Think that you should have a deletefor every object allocated with new.

认为您应该delete为每个分配有new.

EDIT

编辑

Come to think of it, object2doesn't have to be a memory leak.

仔细想想,object2不一定是内存泄漏。

The following code is just to make a point, it's a bad idea, don't ever like code like this:

以下代码只是为了说明这一点,这是一个坏主意,永远不要喜欢这样的代码:

class B
{
public:
    B() {};   //default constructor
    B(const B& other) //copy constructor, this will be called
                      //on the line B object2 = *(new B())
    {
        delete &other;
    }
}

In this case, since otheris passed by reference, it will be the exact object pointed to by new B(). Therefore, getting its address by &otherand deleting the pointer would free the memory.

在这种情况下,由于other通过引用传递,它将是 指向的确切对象new B()。因此,通过获取其地址&other并删除指针将释放内存。

But I can't stress this enough, don't do this. It's just here to make a point.

但我不能强调这一点,不要这样做。只是在这里提出一个观点。

回答by Pubby

Given two "objects":

给定两个“对象”:

obj a;
obj b;

They won't occupy the same location in memory. In other words, &a != &b

它们不会在内存中占据相同的位置。换句话说,&a != &b

Assigning the value of one to the other won't change their location, but it will change their contents:

将一个的值分配给另一个不会改变它们的位置,但会改变它们的内容:

obj a;
obj b = a;
//a == b, but &a != &b

Intuitively, pointer "objects" work the same way:

直观地说,指针“对象”的工作方式相同:

obj *a;
obj *b = a;
//a == b, but &a != &b


Now, let's look at your example:

现在,让我们看看你的例子:

A *object1 = new A();

This is assigning the value of new A()to object1. The value is a pointer, meaning object1 == new A(), but &object1 != &(new A()). (Note that this example is not valid code, it is only for explanation)

这是分配new A()to的值object1。该值是一个指针,意思是object1 == new A(),但是&object1 != &(new A())。(注意此示例不是有效代码,仅用于说明)

Because the value of the pointer is preserved, we can free the memory it points to: delete object1;Due to our rule, this behaves the same as delete (new A());which has no leak.

因为保留了指针的值,我们可以释放它指向的内存:delete object1;由于我们的规则,这与delete (new A());没有泄漏的行为相同。



For you second example, you are copying the pointed-to object. The value is the contents of that object, not the actual pointer. As in every other case, &object2 != &*(new A()).

对于第二个示例,您正在复制指向的对象。该值是该对象的内容,而不是实际的指针。与其他所有情况一样,&object2 != &*(new A()).

B object2 = *(new B());

We have lost the pointer to the allocated memory, and thus we cannot free it. delete &object2;may seem like it would work, but because &object2 != &*(new A()), it is not equivalent to delete (new A())and so invalid.

我们丢失了指向已分配内存的指针,因此无法释放它。delete &object2;可能看起来它会起作用,但是因为&object2 != &*(new A()),它不等同于delete (new A())所以无效。

回答by MGZero

B object2 = *(new B());

This line is the cause of the leak. Let's pick this apart a bit..

这条线是泄漏的原因。让我们把它分开一点..

object2 is a variable of type B, stored at say address 1 (Yes, I'm picking arbitrary numbers here). On the right side, you've asked for a new B, or a pointer to an object of type B. The program gladly gives this to you and assigns your new B to address 2 and also creates a pointer in address 3. Now, the only way to access the data in address 2 is via the pointer in address 3. Next, you dereferenced the pointer using *to get the data that the pointer is pointing to (the data in address 2). This effectively creates a copy of that data and assigns it to object2, assigned in address 1. Remember, it's a COPY, not the original.

object2 是 B 类型的变量,存储在地址 1(是的,我在这里选择任意数字)。在右侧,你要求一个新的 B,或者一个指向 B 类型对象的指针。程序很高兴地给你这个,并将你的新 B 分配给地址 2,并在地址 3 中创建一个指针。现在,访问地址 2 中数据的唯一方法是通过地址 3 中的指针。接下来,您使用取消引用指针*来获取指针指向的数据(地址 2 中的数据)。这有效地创建了该数据的副本并将其分配给在地址 1 中分配的 object2。请记住,它是一个 COPY,而不是原始数据。

Now, here's the problem:

现在,问题来了:

You never actually stored that pointer anywhere you can use it! Once this assignment is finished, the pointer (memory in address3, which you used to access address2) is out of scope and beyond your reach! You can no longer call delete on it and therefore cannot clean up the memory in address2. What you are left with is a copy of the data from address2 in address1. Two of the same things sitting in memory. One you can access, the other you can't (because you lost the path to it). That's why this is a memory leak.

您实际上从未将该指针存储在任何可以使用它的地方!一旦这个分配完成,指针(地址 3 中的内存,你用来访问地址 2 的内存)超出范围并且超出你的范围!您无法再对其调用 delete,因此无法清理 address2 中的内存。剩下的是 address1 中 address2 的数据副本。记忆中的两件相同的事情。一个你可以访问,另一个你不能(因为你失去了通往它的路径)。这就是为什么这是内存泄漏。

I would suggest coming from your C# background that you read up a lot on how pointers in C++ work. They are an advanced topic and can take some time to grasp, but their use will be invaluable to you.

我建议您从 C# 背景出发,阅读大量有关 C++ 中指针如何工作的信息。它们是一个高级主题,可能需要一些时间才能掌握,但它们的使用对您来说是无价的。

回答by CashCow

In C# and Java, you use new to create an instance of any class and then you do not need to worry about destroying it later.

在 C# 和 Java 中,您可以使用 new 创建任何类的实例,然后您无需担心稍后销毁它。

C++ also has a keyword "new" which creates an object but unlike in Java or C#, it is not the only way to create an object.

C++ 也有一个关键字“new”,它创建一个对象,但与 Java 或 C# 不同,它不是创建对象的唯一方法。

C++ has two mechanisms to create an object:

C++有两种创建对象的机制:

  • automatic
  • dynamic
  • 自动的
  • 动态的

With automatic creation you create the object in a scoped environment: - in a function or - as a member of a class (or struct).

通过自动创建,您可以在作用域环境中创建对象: - 在函数中或 - 作为类(或结构)的成员。

In a function you would create it this way:

在函数中,您可以这样创建它:

int func()
{
   A a;
   B b( 1, 2 );
}

Within a class you would normally create it this way:

在一个类中,您通常会以这种方式创建它:

class A
{
  B b;
public:
  A();
};    

A::A() :
 b( 1, 2 )
{
}

In the first case, the objects are destroyed automatically when the scope block is exited. This could be a function or a scope-block within a function.

在第一种情况下,当退出范围块时,对象会自动销毁。这可以是函数或函数内的作用域块。

In the latter case the object b is destroyed together with the instance of A in which it is a member.

在后一种情况下,对象 b 与其作为成员的 A 的实例一起被销毁。

Objects are allocated with new when you need to control the lifetime of the object and then it requires delete to destroy it. With the technique known as RAII, you take care of the deletion of the object at the point you create it by putting it within an automatic object, and wait for that automatic object's destructor to take effect.

当您需要控制对象的生命周期时,使用 new 分配对象,然后需要 delete 来销毁它。使用称为 RAII 的技术,您可以通过将对象放入自动对象中来在创建对象时删除该对象,并等待该自动对象的析构函数生效。

One such object is a shared_ptr which will invoke a "deleter" logic but only when all the instances of the shared_ptr that are sharing the object are destroyed.

一个这样的对象是 shared_ptr ,它将调用“删除器”逻辑,但只有当共享该对象的 shared_ptr 的所有实例都被销毁时。

In general, whilst your code may have many calls to new, you should have limited calls to delete and should always make sure these are called from destructors or "deleter" objects that are put into smart-pointers.

一般来说,虽然您的代码可能有很多对 new 的调用,但您应该对 delete 进行有限的调用,并且应始终确保这些调用是从放入智能指针的析构函数或“删除器”对象中调用的。

Your destructors should also never throw exceptions.

你的析构函数也不应该抛出异常。

If you do this, you will have few memory leaks.

如果你这样做,你将很少有内存泄漏。

回答by Stefan

If it makes it easier, think of computer memory as being like a hotel and programs are customers who hire rooms when they need them.

如果它更容易,可以把计算机内存想象成一家旅馆,程序就是在需要时租用房间的客户。

The way this hotel works is that you book a room and tell the porter when you are leaving.

这家酒店的运作方式是您预订房间并在您离开时告诉行李员。

If you program books a room and leaves without telling the porter the porter will think that the room is still is use and will not let anyone else use it. In this case there is a room leak.

如果您编程预订房间并离开而不告诉搬运工,搬运工会认为房间仍在使用,并且不会让其他任何人使用它。在这种情况下,房间漏水。

If your program allocates memory and does not delete it (it merely stops using it) then the computer thinks that the memory is still in use and will not allow anyone else to use it. This is a memory leak.

如果您的程序分配了内存并且没有删除它(它只是停止使用它),那么计算机认为该内存仍在使用中并且不会允许其他任何人使用它。这是内存泄漏。

This is not an exact analogy but it might help.

这不是一个确切的类比,但它可能会有所帮助。

回答by Mario

When creating object2you're creating a copy of the object you created with new, but you're also losing the (never assigned) pointer (so there's no way to delete it later on). To avoid this, you'd have to make object2a reference.

创建时,object2您正在创建使用 new 创建的对象的副本,但您也丢失了(从未分配过的)指针(因此以后无法删除它)。为了避免这种情况,你必须做object2一个参考。

回答by mattjgalloway

It's this line that is immediately leaking:

这是立即泄漏的这一行:

B object2 = *(new B());

Here you are creating a new Bobject on the heap, then creating a copy on the stack. The one that has been allocated on the heap can no longer be accessed and hence the leak.

在这里,您将B在堆上创建一个新对象,然后在堆栈上创建一个副本。已经在堆上分配的那个不能再访问,因此泄漏。

This line is not immediately leaky:

这条线不会立即泄漏:

A *object1 = new A();

There would be a leak if you never deleted object1though.

会有泄漏,如果你从来没有deleteðobject1虽然。

回答by razlebe

Well, you create a memory leak if you don't at some point free the memory you've allocated using the newoperator by passing a pointer to that memory to the deleteoperator.

好吧,如果您在某个时候没有new通过将指向该内存的指针传递给运算符来释放使用该运算符分配的内存,则会造成内存泄漏delete

In your two cases above:

在上述两种情况下:

A *object1 = new A();

Here you aren't using deleteto free the memory, so if and when your object1pointer goes out of scope, you'll have a memory leak, because you'll have lost the pointer and so can't use the deleteoperator on it.

在这里,您没有使用delete释放内存,因此如果您的object1指针超出范围,就会发生内存泄漏,因为您将丢失指针,因此无法delete在其上使用运算符。

And here

和这里

B object2 = *(new B());

you are discarding the pointer returned by new B(), and so can never pass that pointer to deletefor the memory to be freed. Hence another memory leak.

您正在丢弃由 返回的指针new B(),因此永远无法将该指针传递delete给要释放的内存。因此又一次内存泄漏。