我已经使用c++有一段时间了,我一直在想这个新的关键字。简单地说,我是否应该使用它?

使用new关键字…

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

没有new关键字…

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

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

困难在于方法1很难与std c++类一起使用。

我应该用哪种方法?

更新1:

最近,我为一个超出作用域(即从函数返回)的大数组使用了用于堆内存(或自由存储)的new关键字。在我使用堆栈之前,会导致一半的元素在作用域外损坏,切换到堆使用可以确保元素完好无损。耶!

更新2:

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

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

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


简短的回答是肯定的,“new”关键字非常重要,因为当你使用它时,对象数据存储在堆上,而不是堆栈上,这是最重要的!


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


方法一(使用new)

为自由存储区上的对象分配内存(这通常与堆相同) 要求稍后显式删除对象。(如果你不删除它,你可能会创建一个内存泄漏) 内存一直被分配,直到删除它。(也就是说,你可以返回一个你用new创建的对象) 问题中的例子会泄漏内存,除非删除指针;无论采用哪条控制路径,或者是否抛出异常,都应该始终删除它。

方法二(不使用new)

为堆栈上的对象分配内存(所有局部变量都在堆栈上)。如果分配的对象太多,就有堆栈溢出的风险。 你以后不需要删除它。 当内存超出作用域时,不再分配内存。(也就是说,你不应该返回指向栈上对象的指针)

至于用哪一个;考虑到上述约束条件,您可以选择最适合自己的方法。

一些简单的例子:

如果不想担心调用delete(以及可能导致内存泄漏),就不应该使用new。 如果你想从函数返回指向对象的指针,你必须使用new


第二个方法在堆栈上创建实例,以及一些声明为int的东西和传递给函数的参数列表。

第一个方法为堆栈上的指针腾出空间,您已经将其设置为内存中已在堆(或自由存储区)上分配新MyClass的位置。

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


如果没有new关键字,你就把它存储在调用堆栈上。在堆栈中存储过大的变量将导致堆栈溢出。


我应该用哪种方法?

这几乎从来不是由您的输入首选项决定的,而是由上下文决定的。如果你需要将对象保存在几个堆栈中,或者它对于堆栈来说太重了,你可以将它分配到免费存储中。此外,由于您正在分配一个对象,您还负责释放内存。查找删除操作符。

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


你是将myClass传递给函数,还是期望它存在于函数之外?正如其他人所说,当您不在堆上分配时,这完全是关于范围的问题。当你离开函数时,它(最终)消失了。初学者常犯的一个经典错误是试图在函数中创建某个类的局部对象,并返回它而不将其分配到堆上。我还记得在我早期使用c++时调试这类事情。


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

也就是说,在一些明显的实例中,堆栈变量是不够的。

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 MB on 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).

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


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

所有没有使用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). 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#的相似之处)

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

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

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

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

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

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

When the function returns, the following will happen: First, b2 goes out of scope (order of destruction is always opposite of order of construction). But b2 is just a pointer, so nothing happens, the memory it occupies is simply freed. And importantly, the memory it points to (the bar instance on the heap) is NOT touched. 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.

那堆上的酒吧侍者呢?它可能还在那里。没人删除它,所以我们泄露了内存。

From this example, we can see that anything with automatic duration is guaranteed to 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.

这就是大多数c++代码的工作原理。 例如,看看标准库的std::vector。它通常在堆栈上分配,但可以动态地调整大小和大小。它通过在必要时在堆上内部分配内存来实现这一点。类的用户永远不会看到这一点,所以不会有泄漏内存的机会,也不会忘记清理分配的内存。

这个原则被称为RAII(资源获取即初始化),它可以扩展到任何必须获取和释放的资源。(网络套接字,文件,数据库连接,同步锁)。所有这些资源都可以在构造函数中获取,并在析构函数中释放,因此可以保证获得的所有资源都将再次被释放。

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


如果你是用c++写的,你可能是为了性能而写的。使用new和free store要比使用堆栈慢得多(特别是在使用线程时),所以只在需要时使用它。

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

另外,尽量避免使用delete。把你的新代码包装成智能指针。让智能指针为你调用删除。

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.


简单的回答是:如果你是c++的初学者,你永远不应该使用new或delete。

相反,你应该使用智能指针,例如std::unique_ptr和std::make_unique(或者很少使用std::shared_ptr和std::make_shared)。这样,您就不必太担心内存泄漏了。即使您更高级,最佳实践通常是将使用new和delete的自定义方式封装到一个专门用于对象生命周期问题的小类(例如自定义智能指针)中。

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.


c++核心指南R.11:避免显式使用new和delete。

自从这个问题的答案被写出来以来,情况已经发生了显著的变化。具体来说,c++已经发展成为一门语言,标准库现在更加丰富。为什么这很重要?因为两个因素的结合:

使用new和delete有潜在的危险:如果你没有在不再使用时删除所有已分配的内容,那么内存可能会泄漏;永远不要删除当前未分配的资源。 标准库现在提供了封装new和delete调用的智能指针,这样你就不必自己管理免费存储/堆的分配了。标准库和其他地方的其他容器也是如此。

正如链接的文档所示,这已经发展成为c++社区编写更好的c++代码的“核心准则”之一。当然,这条规则也有例外:有人需要编写那些使用new和delete的封装类;但那个人很少是你自己。

补充@DanielSchepler的有效答案: