简体   繁体   English

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

[英]When should I use the new keyword in C++?

I've been using C++ for a short while, and I've been wondering about the new keyword.我使用 C++ 已经有一段时间了,我一直想知道new关键字。 Simply, should I be using it, or not?简单地说,我应该使用它吗?

  1. With the new keyword...使用new关键字...
    MyClass* myClass = new MyClass();
    myClass->MyField = "Hello world!";
  1. Without the new keyword...没有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 new keyword for heap memory (or free store ) for a large array which was going out of scope (ie being returned from a function).我最近使用new关键字memory (或免费存储)用于一个大数组,该数组将超出 scope (即从函数返回)。 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 intact.在我使用堆栈之前,这会导致 scope 之外的一半元素损坏,切换到堆使用可确保元素完好无损。 Yay!好极了!

Update 2:更新 2:

A friend of mine recently told me there's a simple rule for using the new keyword;我的一个朋友最近告诉我使用new关键字有一个简单的规则; every time you type new , type delete .每次键入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 (ie when you cut and paste it to either a destructor or otherwise).这有助于防止 memory 泄漏,因为您总是必须将删除放在某个地方(即当您将其剪切并粘贴到析构函数或其他地方时)。

Method 1 (using new )方法 1(使用new

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

Method 2 (not using new )方法2(不使用new

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

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 .如果您不想担心调用delete (以及可能导致memory 泄漏),则不应使用new
  • If you'd like to return a pointer to your object from a function, you must use new如果您想从 function 返回指向 object 的指针,则必须使用new

There is an important difference between the two.两者之间有一个重要的区别。

Everything not allocated with new behaves 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 new have automatic storage duration Everything allocated with new is allocated on the heap, and a pointer to it is returned, exactly like reference types in C#.未使用new分配的所有内容都与 C# 中的值类型非常相似(人们常说这些对象是在堆栈上分配的,这可能是最常见/最明显的情况,但并非总是如此。更准确地说,不使用new分配的对象具有自动存储持续时间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).在堆栈上分配的任何内容都必须具有在编译时确定的恒定大小(编译器必须正确设置堆栈指针,或者如果 object 是另一个 class 的成员,它必须调整其他类的大小) . That's why arrays in C# are reference types.这就是为什么 C# 中的 arrays 是引用类型的原因。 They have to be, because with reference types, we can decide at runtime how much memory to ask for.它们必须是,因为使用引用类型,我们可以在运行时决定要请求多少 memory。 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).只有具有恒定大小(可以在编译时确定的大小)的 arrays 可以分配自动存储持续时间(在堆栈上)。 Dynamically sized arrays have to be allocated on the heap, by calling new .动态大小的 arrays 必须通过调用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:这个 function 创建了三个值得考虑的值:

On line 1, it declares a variable b of type bar on the stack (automatic duration).在第 1 行,它在堆栈上声明了一个bar类型的变量b (自动持续时间)。

On line 2, it declares a bar pointer b2 on the stack (automatic duration), and calls new, allocating a bar object on the heap.在第 2 行,它在堆栈上声明了一个bar指针b2 (自动持续时间),调用 new,在堆上分配一个bar object。 (dynamic duration) (动态持续时间)

When the function returns, the following will happen: First, b2 goes out of scope (order of destruction is always opposite of order of construction).当function返回时,会发生以下情况:首先, b2走出scope(破坏顺序总是与构造顺序相反)。 But b2 is just a pointer, so nothing happens, the memory it occupies is simply freed.但是b2只是一个指针,所以什么也没有发生,它占用的 memory 被简单地释放了。 And importantly, the memory it points to (the bar instance on the heap) is NOT touched.重要的是,它指向的 memory (堆上的bar实例)没有被触及。 Only the pointer is freed, because only the pointer had automatic duration.只有指针被释放,因为只有指针具有自动持续时间。 Second, b goes out of scope, so since it has automatic duration, its destructor is called, and the memory is freed.其次, b从scope出来,所以由于它有自动持续时间,所以调用它的析构函数,释放memory。

And the bar instance on the heap?堆上的bar实例呢? It's probably still there.它可能还在那里。 No one bothered to delete it, so we've leaked memory.没有人愿意删除它,所以我们泄露了 memory。

From this example, we can see that anything with automatic duration is guaranteed to have its destructor called when it goes out of scope.从这个例子中,我们可以看到任何具有自动持续时间的东西都保证在它超出 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.但是在堆上分配的任何东西只要我们需要它就可以持续,并且可以动态调整大小,例如 arrays。 That is also useful.这也很有用。 We can use that to manage our memory allocations.我们可以使用它来管理我们的 memory 分配。 What if the Foo class allocated some memory on the heap in its constructor, and deleted that memory in its destructor.如果 Foo class 在其构造函数的堆上分配了一些 memory 并在其析构函数中删除了 memory 怎么办。 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.然后我们可以两全其美,安全的 memory 分配保证再次被释放,但没有强制所有内容都在堆栈上的限制。

And that is pretty much exactly how most C++ code works.这几乎就是大多数 C++ 代码的工作原理。 Look at the standard library's std::vector for example.以标准库的std::vector为例。 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.它通过在必要时在堆上内部分配 memory 来实现这一点。 The user of the class never sees this, so there's no chance of leaking memory, or forgetting to clean up what you allocated. class 的用户永远不会看到这一点,因此不会泄漏 memory,或者忘记清理您分配的内容。

This principle is called RAII (Resource Acquisition is Initialization), and it can be extended to any resource that must be acquired and released.这个原理叫做RAII(Resource Acquisition is Initialization),它可以扩展到任何必须被获取和释放的资源。 (network sockets, files, database connections, synchronization locks). (网络sockets,文件,数据库连接,同步锁)。 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.它们都可以在构造函数中获取,并在析构函数中释放,因此您可以保证您获得的所有资源都会再次被释放。

As a general rule, never use new/delete directly from your high level code.作为一般规则,永远不要直接从您的高级代码中使用 new/delete。 Always wrap it in a class that can manage the memory for you, and which will ensure it gets freed again.始终将其包装在 class 中,它可以为您管理 memory,这将确保它再次被释放。 (Yes, there may be exceptions to this rule. In particular, smart pointers require you to call new directly, and pass the pointer to its constructor, which then takes over and ensures delete is called correctly. But this is still a very important rule of thumb) (是的,这条规则可能有例外。特别是,智能指针要求您直接调用new ,并将指针传递给其构造函数,然后由构造函数接管并确保正确调用delete 。但这仍然是一个非常重要的规则拇指)

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.如果您需要将 object 保留在几个堆栈中,或者如果堆栈对于堆栈来说太重,您可以在免费存储中分配它。 Also, since you are allocating an object, you are also responsible for releasing the memory.此外,由于您分配的是 object,因此您还负责释放 memory。 Lookup the delete operator.查找delete运算符。

To ease the burden of using free-store management people have invented stuff like auto_ptr and unique_ptr .为了减轻使用免费商店管理的负担,人们发明了诸如auto_ptrunique_ptr之类的东西。 I strongly recommend you take a look at these.我强烈建议你看看这些。 They might even be of help to your typing issues;-)它们甚至可能对您的打字问题有所帮助;-)

If you are writing in C++ you are probably writing for performance.如果您使用 C++ 编写代码,您可能是为了性能而编写代码。 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.使用 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. 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.

Also, try to avoid ever using delete.另外,尽量避免使用删除。 Wrap your new into a smart pointer instead.将你的 new 包装成一个智能指针。 Let the smart pointer call delete for you.让智能指针为你调用 delete。

There are some cases where a smart pointer isn't smart.在某些情况下,智能指针不智能。 Never store std::auto_ptr<> inside a STL container.切勿将 std::auto_ptr<> 存储在 STL 容器中。 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.另一种情况是当你有一个非常大的 STL 指向对象的容器时。 boost::shared_ptr<> will have a ton of speed overhead as it bumps the reference counts up and down. boost::shared_ptr<> 会产生大量的速度开销,因为它会上下颠簸引用计数。 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. 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.

The short answer is: if you're a beginner in C++, you should never be using new or delete yourself.简短的回答是:如果您是 C++ 的初学者,您永远不应该使用newdelete自己。

Instead, you should use smart pointers such as std::unique_ptr and std::make_unique (or less often, std::shared_ptr and std::make_shared ).相反,您应该使用智能指针,例如std::unique_ptrstd::make_unique (或者较少使用std::shared_ptrstd::make_shared )。 That way, you don't have to worry nearly as much about memory leaks.这样,您就不必担心 memory 泄漏。 And even if you're more advanced, best practice would usually be to encapsulate the custom way you're using new and delete into a small class (such as a custom smart pointer) that is dedicated just to object lifecycle issues.即使您更高级,最佳实践通常是将您使用newdelete的自定义方式封装到专门用于 object 生命周期问题的小型 class(例如自定义智能指针)中。

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.这里的其他答案已经涵盖了这些问题,以及如何就何时使用智能指针而不是仅在堆栈上创建对象或将它们合并为 object 的直接成员做出设计决策,足够好,我不会重复它们。 But my executive summary would be: don't use smart pointers or dynamic allocation until something forces you to.但我的执行摘要是:在某些事情迫使您使用之前,不要使用智能指针或动态分配。

Without the new keyword you're storing that on call stack .如果没有new关键字,您会将其存储在call stack上。 Storing excessively large variables on stack will lead to stack overflow .在堆栈上存储过大的变量会导致堆栈溢出

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() 在堆上创建一个 object (不幸的副作用是你必须管理它的生命周期(通过显式调用它),而第二种形式在当前的堆栈中创建一个 object scope 和 object 在离开 scope 时将被销毁。

Are you passing myClass out of a function, or expecting it to exist outside that function?您是从 function 中传递 myClass,还是期望它存在于 function 之外? As some others said, it is all about scope when you aren't allocating on the heap.正如其他人所说,当您不在堆上分配时,这都是关于 scope 的。 When you leave the function, it goes away (eventually).当您离开 function 时,它会消失(最终)。 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.初学者犯的经典错误之一是尝试在 function 中创建一些 class 的本地 object 并返回它而不在堆上分配它。 I can remember debugging this kind of thing back in my earlier days doing c++.我还记得在我早期做 c++ 时调试过这种事情。

If your variable is used only within the context of a single function, you're better off using a stack variable, ie, Option 2. As others have said, you do not have to manage the lifetime of stack variables - they are constructed and destructed automatically.如果您的变量仅在单个 function 的上下文中使用,则最好使用堆栈变量,即选项 2。正如其他人所说,您不必管理堆栈变量的生命周期 - 它们是构造和自动销毁。 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.如果您的 function 被调用得足够频繁,那么如果使用堆栈变量与堆变量相比,您将看到巨大的性能提升。

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.如果堆栈变量具有较大的 memory 占用空间,那么您将面临堆栈溢出的风险。 By default, the stack size of each thread is 1 MB on Windows.默认情况下,Windows上每个线程的堆栈大小为 1 MB 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.您不太可能创建大小为 1 MB 的堆栈变量,但您必须记住堆栈利用率是累积的。 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. 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).如果这是一个问题,您可以增加堆栈的大小(不推荐)或使用 new 运算符在堆上分配变量(推荐)。

The other, more likely condition is that your variable needs to "live" beyond the scope of your function.另一种更可能的情况是,您的变量需要在 function 的 scope 之外“存活”。 In this case, you'd allocate the variable on the heap so that it can be reached outside the scope of any given function.在这种情况下,您将在堆上分配变量,以便可以在任何给定 function 的 scope 之外访问它。

C++ Core Guidelines R.11 : Avoid using new and delete explicitly. C++ 核心指南 R.11 :避免明确使用newdelete

Things have changed significantly since most answers to this question were written.自从写下这个问题的大多数答案以来,情况发生了重大变化。 Specifically, C++ has evolved as a language, and the standard library is now richer.具体来说,C++已经发展成为一种语言,标准库现在更加丰富。 Why does this matter?为什么这很重要? Because of a combination of two factors:由于两个因素的结合:

  • Using new and delete is potentially dangerous: Memory might leak if you don't keep a very strong discipline of delete 'ing everything you've allocated when it's no longer used;使用newdelete有潜在的危险:如果您不严格遵守delete原则,在不再使用分配的所有内容时,Memory 可能会泄漏; and never delete ing what's not currently allocated.永远不要delete当前未分配的内容。
  • The standard library now offers smart pointers which encapsulate the new and delete calls, so that you don't have to take care of managing allocations on the free store/heap yourself.标准库现在提供了封装newdelete调用的智能指针,这样您就不必自己管理自由存储/堆上的分配。 So do other containers, in the standard library and elsewhere.标准库和其他地方的其他容器也是如此。

This has evolved into one of the C++ community's "core guidelines" for writing better C++ code, as the linked document shows.如链接文档所示,这已发展成为 C++ 社区编写更好 C++ 代码的“核心指南”之一。 Of course, there exceptions to this rule: Somebody needs to write those encapsulating classes which do use new and delete ;当然,这条规则也有例外:有人需要编写那些确实使用newdelete的封装类; but that someone is rarely yourself.但很少有人是你自己。

Adding to @DanielSchepler's valid answer:添加到@DanielSchepler 的有效答案:

The short answer is yes the "new" keyword is incredibly important as when you use it the object data is stored on the heap as opposed to the stack, which is most important!简短的回答是肯定的,“new”关键字非常重要,因为当您使用它时,object 数据存储在堆上而不是堆栈上,这是最重要的!

The second method creates the instance on the stack, along with such things as something declared int and the list of parameters that are passed into the function.第二种方法在堆栈上创建实例,以及诸如声明为int的内容以及传递到 function 的参数列表等内容。

The first method makes room for a pointer on the stack, which you've set to the location in memory where a new MyClass has been allocated on the heap - or free store.第一种方法为堆栈上的指针腾出空间,您已将其设置为 memory 中已在堆或空闲存储上分配新MyClass的位置。

The first method also requires that you delete what 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创建的内容,而在第二种方法中,当 class 掉出 scope (通常是下一个右大括号)时,它会自动销毁和释放。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM