我什么时候应该在 C++ 中使用 new 关键字?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/655065/
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-27 16:32:48  来源:igfitidea点击:

When should I use the new keyword in C++?

c++pointersreferencenew-operatorkeyword

提问by Nick Bolton

I've been using C++ for a short while, and I've been wondering about the newkeyword. Simply, should I be using it, or not?

我使用 C++ 已经有一段时间了,我一直在想new关键字。简单地说,我应该使用它还是不使用它?

1) With the newkeyword...

1) 使用new关键字...

MyClass* myClass = new MyClass();
myClass->MyField = "Hello world!";

2) Without the newkeyword...

2) 没有new关键字...

MyClass myClass;
myClass.MyField = "Hello world!";

From an implementation perspective, they don't seem that different (but I'm sure they are)... However, my primary language is C#, and of course the 1st method is what I'm used to.

从实现的角度来看,它们似乎没有什么不同(但我确信它们是)……但是,我的主要语言是 C#,当然第一种方法是我习惯的方法。

The difficulty seems to be that method 1 is harder to use with the std C++ classes.

困难似乎是方法 1 更难与 std C++ 类一起使用。

Which method should I use?

我应该使用哪种方法?

Update 1:

更新 1:

I recently used the newkeyword for heapmemory (or free store) for a large array which was going out of scope (i.e. being returned from a function). Where before I was using the stack, which caused half of the elements to be corrupt outside of scope, switching to heap usage ensured that the elements were in tact. Yay!

我最近将new关键字用于内存(或free store),用于超出范围(即从函数返回)的大型数组。在我使用堆栈之前,它导致一半元素在范围之外损坏,切换到堆使用确保元素完好无损。好极了!

Update 2:

更新 2:

A friend of mine recently told me there's a simple rule for using the newkeyword; every time you type new, type delete.

我的一个朋友最近告诉我使用new关键字有一个简单的规则;每次键入时new,键入delete.

Foobar *foobar = new Foobar();
delete foobar; // TODO: Move this to the right place.

This helps to prevent memory leaks, as you always have to put the delete somewhere (i.e. when you cut and paste it to either a destructor or otherwise).

这有助于防止内存泄漏,因为您总是必须将删除放在某处(即,当您将其剪切并粘贴到析构函数或其他地方时)。

回答by Daniel LeCheminant

Method 1 (using new)

方法一(使用new

  • Allocates memory for the object on the free store(This is frequently the same thing as the heap)
  • Requires you to explicitly deleteyour object later. (If you don't delete it, you could create a memory leak)
  • Memory stays allocated until you deleteit. (i.e. you could returnan object that you created using new)
  • The example in the question will leak memoryunless the pointer is deleted; and it should always be deleted, regardless of which control path is taken, or if exceptions are thrown.
  • 空闲存储上的对象分配内存 (这通常与相同)
  • 要求您delete稍后明确您的对象。(如果你不删除它,你可能会造成内存泄漏)
  • 内存一直分配到你delete为止。(即您可以return使用创建的对象new
  • 问题中的示例将泄漏内存,除非指针为deleted;它应该始终被删除,无论采用哪个控制路径,或者是否抛出异常。

Method 2 (not using new)

方法二(不使用new

  • Allocates memory for the object on the stack(where all local variables go) There is generally less memory available for the stack; if you allocate too many objects, you risk stack overflow.
  • You won't need to deleteit later.
  • Memory is no longer allocated when it goes out of scope. (i.e. you shouldn't returna pointer to an object on the stack)
  • 堆栈上的对象分配内存(所有局部变量所在的位置) 堆栈可用的内存通常较少;如果您分配太多对象,则会面临堆栈溢出的风险。
  • delete以后就不需要了。
  • 超出范围时不再分配内存。(即你不应该return指向堆栈上的对象)

As far as which one to use; you choose the method that works best for you, given the above constraints.

至于使用哪一个;考虑到上述限制,您可以选择最适合您的方法。

Some easy cases:

一些简单的案例:

  • If you don't want to worry about calling delete, (and the potential to cause memory leaks) you shouldn't use new.
  • If you'd like to return a pointer to your object from a function, you must use new
  • 如果您不想担心调用delete, (以及可能导致内存泄漏),则不应使用new.
  • 如果你想从一个函数返回一个指向你的对象的指针,你必须使用 new

回答by jalf

There is an important difference between the two.

两者之间有一个重要的区别。

Everything not allocated with newbehaves much like value types in C# (and people often say that those objects are allocated on the stack, which is probably the most common/obvious case, but not always true. More precisely, objects allocated without using newhave automatic storage durationEverything allocated with newis allocated on the heap, and a pointer to it is returned, exactly like reference types in C#.

所有未分配的new行为都与 C# 中的值类型非常相似(人们常说这些对象是在堆栈上分配的,这可能是最常见/最明显的情况,但并非总是如此。更准确地说,未使用分配的对象new具有自动存储duration分配的所有内容都new在堆上分配,并返回指向它的指针,与 C# 中的引用类型完全一样。

Anything allocated on the stack has to have a constant size, determined at compile-time (the compiler has to set the stack pointer correctly, or if the object is a member of another class, it has to adjust the size of that other class). That's why arrays in C# are reference types. They have to be, because with reference types, we can decide at runtime how much memory to ask for. And the same applies here. Only arrays with constant size (a size that can be determined at compile-time) can be allocated with automatic storage duration (on the stack). Dynamically sized arrays have to be allocated on the heap, by calling new.

堆栈上分配的任何内容都必须具有恒定大小,在编译时确定(编译器必须正确设置堆栈指针,或者如果该对象是另一个类的成员,则必须调整该另一个类的大小) . 这就是为什么 C# 中的数组是引用类型的原因。它们必须是,因为使用引用类型,我们可以在运行时决定需要多少内存。这同样适用于这里。只有具有恒定大小(可以在编译时确定的大小)的数组才能分配自动存储持续时间(在堆栈上)。动态大小的数组必须通过调用在堆上分配new

(And that's where any similarity to C# stops)

(这就是与 C# 的任何相似之处停止的地方)

Now, anything allocated on the stack has "automatic" storage duration (you can actually declare a variable as auto, but this is the default if no other storage type is specified so the keyword isn't really used in practice, but this is where it comes from)

现在,堆栈上分配的任何内容都具有“自动”存储持续时间(您实际上可以将变量声明为auto,但是如果没有指定其他存储类型,则这是默认值,因此实际上并未真正使用该关键字,但这就是它的地方来自)

Automatic storage duration means exactly what it sounds like, the duration of the variable is handled automatically. By contrast, anything allocated on the heap has to be manually deleted by you. Here's an example:

自动存储持续时间的意思就像听起来一样,变量的持续时间是自动处理的。相比之下,堆上分配的任何内容都必须由您手动删除。下面是一个例子:

void foo() {
  bar b;
  bar* b2 = new bar();
}

This function creates three values worth considering:

该函数创建了三个值得考虑的值:

On line 1, it declares a variable bof type baron the stack (automatic duration).

在第 1 行,它在堆栈上声明了一个b类型变量bar(自动持续时间)。

On line 2, it declares a barpointer b2on the stack (automatic duration), andcalls new, allocating a barobject on the heap. (dynamic duration)

在第 2 行,它在堆栈上声明一个bar指针b2(自动持续时间),调用 new,bar在堆上分配一个对象。(动态持续时间)

When the function returns, the following will happen: First, b2goes out of scope (order of destruction is always opposite of order of construction). But b2is just a pointer, so nothing happens, the memory it occupies is simply freed. And importantly, the memory it points to(the barinstance on the heap) is NOT touched. Only the pointer is freed, because only the pointer had automatic duration. Second, bgoes out of scope, so since it has automatic duration, its destructor is called, and the memory is freed.

当函数返回时,将发生以下情况:首先,b2超出范围(销毁顺序始终与构造顺序相反)。但b2只是一个指针,所以什么也没有发生,它占用的内存只是被释放了。重要的是,它指向的内存(bar堆上的实例)没有被触及。只有指针被释放,因为只有指针有自动持续时间。其次,b超出范围,因此由于它具有自动持续时间,因此会调用其析构函数,并释放内存。

And the barinstance on the heap? It's probably still there. No one bothered to delete it, so we've leaked memory.

bar堆上的实例呢?它可能还在那里。没有人费心删除它,所以我们泄漏了内存。

From this example, we can see that anything with automatic duration is guaranteedto have its destructor called when it goes out of scope. That's useful. But anything allocated on the heap lasts as long as we need it to, and can be dynamically sized, as in the case of arrays. That is also useful. We can use that to manage our memory allocations. What if the Foo class allocated some memory on the heap in its constructor, and deleted that memory in its destructor. Then we could get the best of both worlds, safe memory allocations that are guaranteed to be freed again, but without the limitations of forcing everything to be on the stack.

从这个例子中,我们可以看到任何具有自动持续时间的东西都保证在超出范围时调用其析构函数。这很有用。但是在堆上分配的任何东西只要我们需要它就可以持续,并且可以动态调整大小,就像数组一样。这也很有用。我们可以使用它来管理我们的内存分配。如果 Foo 类在其构造函数中在堆上分配了一些内存,并在其析构函数中删除了该内存,该怎么办?然后我们可以两全其美,保证再次释放的安全内存分配,但没有强制所有内容都在堆栈上的限制。

And that is pretty much exactly how most C++ code works. Look at the standard library's std::vectorfor example. That is typically allocated on the stack, but can be dynamically sized and resized. And it does this by internally allocating memory on the heap as necessary. The user of the class never sees this, so there's no chance of leaking memory, or forgetting to clean up what you allocated.

这几乎就是大多数 C++ 代码的工作方式。以标准库std::vector为例。这通常在堆栈上分配,但可以动态调整大小和调整大小。它通过根据需要在堆上内部分配内存来做到这一点。类的用户永远不会看到这一点,因此不会出现内存泄漏或忘记清理您分配的内容。

This principle is called RAII (Resource Acquisition is Initialization), and it can be extended to any resource that must be acquired and released. (network sockets, files, database connections, synchronization locks). All of them can be acquired in the constructor, and released in the destructor, so you're guaranteed that all resources you acquire will get freed again.

这个原理叫做RAII(Resource Acquisition is Initialization),它可以扩展到任何必须获取和释放的资源。(网络套接字、文件、数据库连接、同步锁)。所有这些都可以在构造函数中获取,并在析构函数中释放,因此您可以保证您获取的所有资源都将再次被释放。

As a general rule, never use new/delete directly from your high level code. Always wrap it in a class that can manage the memory for you, and which will ensure it gets freed again. (Yes, there may be exceptions to this rule. In particular, smart pointers require you to call newdirectly, and pass the pointer to its constructor, which then takes over and ensures deleteis called correctly. But this is still a very important rule of thumb)

作为一般规则,切勿直接从高级代码中使用 new/delete。始终将它包装在一个可以为您管理内存的类中,这将确保它再次被释放。(是的,这个规则可能有例外。特别是,智能指针要求你new直接调用,并将指针传递给它的构造函数,然后它接管并确保delete被正确调用。但这仍然是一个非常重要的经验法则)

回答by dirkgently

Which method should I use?

我应该使用哪种方法?

This is almost never determined by your typing preferences but by the context. If you need to keep the object across a few stacks or if it's too heavy for the stack you allocate it on the free store. Also, since you are allocating an object, you are also responsible for releasing the memory. Lookup the deleteoperator.

这几乎不是由您的打字偏好决定的,而是由上下文决定的。如果您需要将对象保存在几个堆栈中,或者如果它对于堆栈来说太重,您可以将它分配到空闲存储中。此外,由于您正在分配一个对象,因此您还负责释放内存。查找delete运算符。

To ease the burden of using free-store management people have invented stuff like auto_ptrand unique_ptr. I strongly recommend you take a look at these. They might even be of help to your typing issues ;-)

为了减轻使用免费商店管理的负担,人们发明了诸如auto_ptr和 之类的东西unique_ptr。我强烈建议你看看这些。它们甚至可能对您的打字问题有所帮助;-)

回答by Zan Lynx

If you are writing in C++ you are probably writing for performance. Using new and the free store is much slower than using the stack (especially when using threads) so only use it when you need it.

如果您使用 C++ 编写代码,那么您可能是为了性能而编写代码。使用 new 和 free 存储比使用堆栈慢得多(尤其是在使用线程时),因此仅在需要时才使用它。

As others have said, you need new when your object needs to live outside the function or object scope, the object is really large or when you don't know the size of an array at compile time.

正如其他人所说,当您的对象需要存在于函数或对象范围之外、对象非常大或者您在编译时不知道数组的大小时,您需要 new。

Also, try to avoid ever using delete. Wrap your new into a smart pointer instead. Let the smart pointer call delete for you.

此外,尽量避免使用删除。而是将您的 new 包装成一个智能指针。让智能指针为您调用删除。

There are some cases where a smart pointer isn't smart. Never store std::auto_ptr<> inside a STL container. It will delete the pointer too soon because of copy operations inside the container. Another case is when you have a really large STL container of pointers to objects. boost::shared_ptr<> will have a ton of speed overhead as it bumps the reference counts up and down. The better way to go in that case is to put the STL container into another object and give that object a destructor that will call delete on every pointer in the container.

在某些情况下,智能指针并不智能。永远不要将 std::auto_ptr<> 存储在 STL 容器中。由于容器内的复制操作,它会过早删除指针。另一种情况是当您有一个非常大的指向对象的指针的 STL 容器时。boost::shared_ptr<> 将有大量的速度开销,因为它会上下颠簸引用计数。在这种情况下,更好的方法是将 STL 容器放入另一个对象中,并为该对象提供一个析构函数,该析构函数将对容器中的每个指针调用 delete。

回答by Daniel Schepler

The short answer is: if you're a beginner in C++, you should neverbe using newor deleteyourself.

简短的回答是:如果你是 C++ 的初学者,你永远不应该使用newdelete你自己。

Instead, you should use smart pointers such as std::unique_ptrand std::make_unique(or less often, std::shared_ptrand std::make_shared). That way, you don't have to worry nearly as much about memory leaks. And even if you're more advanced, best practice would usually be to encapsulate the custom way you're using newand deleteinto a small class (such as a custom smart pointer) that is dedicated just to object lifecycle issues.

相反,您应该使用智能指针,例如std::unique_ptrstd::make_unique(或较少使用std::shared_ptrstd::make_shared)。这样,您几乎不必担心内存泄漏。而且,即使你是更先进的,最好的做法通常是封装你使用自定义的方式newdelete成小类(如自定义智能指针),专门只是对象生命周期的问题。

Of course, behind the scenes, these smart pointers are still performing dynamic allocation and deallocation, so code using them would still have the associated runtime overhead. Other answers here have covered these issues, and how to make design decisions on when to use smart pointers versus just creating objects on the stack or incorporating them as direct members of an object, well enough that I won't repeat them. But my executive summary would be: don't use smart pointers or dynamic allocation until something forces you to.

当然,在幕后,这些智能指针仍在执行动态分配和释放,因此使用它们的代码仍然会有相关的运行时开销。这里的其他答案已经涵盖了这些问题,以及如何就何时使用智能指针与仅在堆栈上创建对象或将它们合并为对象的直接成员做出设计决策,我不会重复它们。但我的执行摘要是:在某些事情迫使您使用之前,不要使用智能指针或动态分配。

回答by Timo Geusch

The simple answer is yes - new() creates an object on the heap (with the unfortunate side effect that you have to manage its lifetime (by explicitly calling delete on it), whereas the second form creates an object in the stack in the current scope and that object will be destroyed when it goes out of scope.

简单的答案是肯定的 - new() 在堆上创建一个对象(不幸的副作用是你必须管理它的生命周期(通过在它上面显式调用 delete),而第二种形式在当前的堆栈中创建一个对象范围,该对象超出范围时将被销毁。

回答by vartec

Without the newkeyword you're storing that on call stack. Storing excessively large variables on stack will lead to stack overflow.

如果没有new关键字,您会将其存储在call stack 上。在堆栈上存储过大的变量会导致堆栈溢出

回答by itsmatt

Are you passing myClass out of a function, or expecting it to exist outside that function? As some others said, it is all about scope when you aren't allocating on the heap. When you leave the function, it goes away (eventually). One of the classic mistakes made by beginners is the attempt to create a local object of some class in a function and return it without allocating it on the heap. I can remember debugging this kind of thing back in my earlier days doing c++.

您是从函数中传递 myClass 还是期望它存在于该函数之外?正如其他一些人所说,当您不在堆上分配时,这完全与范围有关。当您离开该功能时,它会消失(最终)。初学者犯的经典错误之一是试图在函数中创建某个类的本地对象并返回它,而没有在堆上分配它。我记得我早年做 C++ 时调试过这种东西。

回答by Matt Davis

If your variable is used only within the context of a single function, you're better off using a stack variable, i.e., Option 2. As others have said, you do not have to manage the lifetime of stack variables - they are constructed and destructed automatically. Also, allocating/deallocating a variable on the heap is slow by comparison. If your function is called often enough, you'll see a tremendous performance improvement if use stack variables versus heap variables.

如果您的变量仅在单个函数的上下文中使用,则最好使用堆栈变量,即选项 2。正如其他人所说,您不必管理堆栈变量的生命周期 - 它们是构造和自动销毁。此外,相比之下,在堆上分配/解除分配变量的速度较慢。如果您的函数被足够频繁地调用,那么如果使用堆栈变量而不是堆变量,您将看到巨大的性能改进。

That said, there are a couple of obvious instances where stack variables are insufficient.

也就是说,有几个明显的例子堆栈变量不足。

If the stack variable has a large memory footprint, then you run the risk of overflowing the stack. By default, the stack size of each thread is 1 MBon Windows. It is unlikely that you'll create a stack variable that is 1 MB in size, but you have to keep in mind that stack utilization is cumulative. If your function calls a function which calls another function which calls another function which..., the stack variables in all of these functions take up space on the same stack. Recursive functions can run into this problem quickly, depending on how deep the recursion is. If this is a problem, you can increase the size of the stack (not recommended) or allocate the variable on the heap using the new operator (recommended).

如果堆栈变量有很大的内存占用,那么您就有堆栈溢出的风险。默认情况下,每个线程的堆栈大小在 Windows 上为 1 MB。您不太可能创建大小为 1 MB 的堆栈变量,但您必须记住堆栈利用率是累积的。如果您的函数调用一个调用另一个函数的函数,该函数调用另一个函数...,则所有这些函数中的堆栈变量都占用同一堆栈上的空间。递归函数可能很快就会遇到这个问题,这取决于递归的深度。如果这是一个问题,您可以增加堆栈的大小(不推荐)或使用 new 运算符(推荐)在堆上分配变量。

The other, more likely condition is that your variable needs to "live" beyond the scope of your function. In this case, you'd allocate the variable on the heap so that it can be reached outside the scope of any given function.

另一个更可能的情况是您的变量需要“存活”在您的函数范围之外。在这种情况下,您将在堆上分配变量,以便可以在任何给定函数的范围之外访问它。

回答by greyfade

The second method creates the instance on the stack, along with such things as something declared intand the list of parameters that are passed into the function.

第二种方法在堆栈上创建实例,以及诸如声明int的东西和传递给函数的参数列表之类的东西。

The first method makes room for a pointeron the stack, which you've set to the location in memory where a new MyClasshas been allocated on the heap - or free store.

第一种方法为堆栈上的指针腾出空间,您已将其设置为内存中MyClass已在堆上分配新的位置或空闲存储的位置。

The first method also requires that you deletewhat you create with new, whereas in the second method, the class is automatically destructed and freed when it falls out of scope (the next closing brace, usually).

第一种方法还要求delete您使用创建的内容new,而在第二种方法中,类在超出范围时会自动销毁并释放(通常是下一个右括号)。