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

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

我已经使用C++了一段时间,我一直在想新的关键字。简单地说,我应该使用它吗?

1)使用新关键字…

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

2)没有新关键字…

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

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

难点似乎在于,方法1与STD C++类相比更难使用。

我应该使用哪种方法?

更新1:

我最近使用了堆内存(或空闲存储)的新关键字来表示超出作用域(即从函数返回)的大型数组。在我使用堆栈之前,其中一半的元素在作用域之外被破坏,切换到堆的使用确保了元素的可操作性。哎呀!

更新2:

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

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

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


方法1(使用new)

  • 为空闲存储上的对象分配内存(这通常与堆相同)
  • 要求稍后显式地删除您的对象。(如果不删除,可能会造成内存泄漏)
  • 内存一直保持分配状态,直到您使用cx1〔1〕它。(也就是说,你可以使用new创建一个对象)
  • 除非指针是deleted,否则问题中的示例将泄漏内存;无论采用哪种控制路径,或是否引发异常,都应始终删除它。

方法2(不使用new)

  • 为堆栈上的对象分配内存(所有局部变量都在这里),通常堆栈的可用内存较少;如果分配的对象太多,则存在堆栈溢出的风险。
  • 以后你就不需要再做了。
  • 当内存超出范围时,将不再分配内存。(也就是说,不应该使用指向堆栈上对象的指针return)

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

一些简单的案例:

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


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

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

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

(这就是C的相似之处)好的。

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

自动存储持续时间的含义与它听起来的完全相同,变量的持续时间是自动处理的。相反,堆上分配的任何内容都必须由您手动删除。下面是一个例子:好的。

1
2
3
4
void foo() {
  bar b;
  bar* b2 = new bar();
}

此函数创建三个值得考虑的值:好的。

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

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

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

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

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

这正是大多数C++代码的工作原理。例如,查看标准库的std::vector。通常在堆栈上分配,但可以动态调整大小和大小。它通过根据需要在堆上内部分配内存来实现这一点。类的用户从来没有看到过这一点,所以没有机会泄漏内存,或者忘记清理您分配的内容。好的。

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

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


Which method should I use?

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

为了减轻使用免费商店管理的负担,人们发明了诸如auto_ptrunique_ptr之类的东西。我强烈建议你看看这些。它们甚至可以帮助您解决输入问题;-)


如果你正在用C++编写,你可能是在写性能。使用new和free存储比使用堆栈慢得多(特别是在使用线程时),所以只在需要时使用它。

正如其他人所说,当您的对象需要不在函数或对象范围内时,或者当您在编译时不知道数组的大小时,您需要新的对象。

另外,尽量避免使用删除。把你的新东西包装成一个智能指针。让智能指针为您调用删除。

有些情况下智能指针不智能。不要将std::auto_ptr<>存储在stl容器中。由于容器内的复制操作,它将很快删除指针。另一种情况是,当您有一个很大的STL容器,其中包含指向对象的指针。当它上下碰撞引用计数时,shared&ptr<>将有大量的速度开销。在这种情况下,更好的方法是将STL容器放入另一个对象,并给该对象一个析构函数,该函数将调用容器中每个指针上的delete。


简短的回答是:如果你是C++初学者,你就不应该自己使用EDCOX1 0或EDCOX1 1。

相反,您应该使用智能指针,如std::unique_ptrstd::make_unique(或更少的情况下,std::shared_ptrstd::make_shared)。这样,您就不必担心内存泄漏了。即使您更高级,最佳实践通常也会将您使用newdelete的自定义方式封装到一个小类(如自定义智能指针)中,该类专门用于对象生命周期问题。

当然,在幕后,这些智能指针仍在执行动态分配和释放,因此使用它们的代码仍然会有相关的运行时开销。这里的其他答案涵盖了这些问题,以及如何决定何时使用智能指针,而不仅仅是在堆栈上创建对象,或者将它们作为对象的直接成员合并,我不会重复它们。但我的执行摘要是:除非有什么东西迫使你这样做,否则不要使用智能指针或动态分配。


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


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

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

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

另一个更可能的情况是,变量需要"活"在函数范围之外。在本例中,您将在堆上分配变量,以便可以在任何给定函数的范围之外访问它。


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


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


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

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

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


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