当使用std::list<std::string>时,我偶然发现了堆栈溢出问题std::string的内存泄漏,其中一个注释说:

不要用那么多new。我看不出你为什么在任何地方都用新的。你可以在c++中通过值创建对象,这是使用该语言的一个巨大优势。您不必分配堆上的所有内容。不要像Java程序员那样思考。

我不太确定他这么说是什么意思。

为什么在c++中应该尽可能多地根据值创建对象,这在内部有什么不同?我误解了答案吗?


当前回答

两个原因:

在这种情况下没有必要。您正在使代码不必要地变得更加复杂。 它在堆上分配空间,这意味着您必须记住稍后删除它,否则将导致内存泄漏。

其他回答

在很大程度上,这是某人将自己的弱点提升为普遍准则。使用new操作符创建对象本身没有任何问题。有一些争论是,你必须遵循一些规则:如果你创建了一个对象,你需要确保它会被销毁。

最简单的方法是在自动存储中创建对象,这样c++就知道在它超出作用域时销毁它:

 {
    File foo = File("foo.dat");

    // Do things

 }

现在,观察一下,当你在结束大括号之后离开那个块时,foo超出了作用域。c++会自动调用它的析构函数。与Java不同,您不需要等待垃圾回收来找到它。

你写过

 {
     File * foo = new File("foo.dat");

你需要显式地匹配它

     delete foo;
  }

或者更好的是,将你的File *分配为“智能指针”。如果你不小心,它可能会导致泄漏。

答案本身做了一个错误的假设,如果你不使用new,你就不会在堆上分配;事实上,在c++中你不知道这一点。最多,您知道少量的内存,比如一个指针,肯定分配在堆栈上。但是,考虑一下File的实现是否像这样:

  class File {
    private:
      FileImpl * fd;
    public:
      File(String fn){ fd = new FileImpl(fn);}

然后FileImpl仍然会被分配到堆栈上。

是的,你最好确保你有

     ~File(){ delete fd ; }

在课堂上也是如此;如果没有它,即使根本没有在堆上分配内存,也会从堆中泄漏内存。

使用new时,对象被分配到堆中。它通常用于预期扩展时。当你声明一个对象,比如,

Class var;

它被放置在堆栈上。

你总是需要对你用new放在堆上的对象调用destroy。这就有可能导致内存泄漏。放在堆栈上的对象不容易发生内存泄漏!

我发现有几个重要的原因让我们忽略了尽量少做新事情:

Operator new的执行时间不确定

调用new可能会导致操作系统为进程分配新的物理页,也可能不会。如果你经常这样做,会很慢。或者它可能已经准备好了一个合适的内存位置;我们不知道。如果你的程序需要具有一致且可预测的执行时间(如在实时系统或游戏/物理模拟中),你需要避免在时间关键型循环中添加新的元素。

操作符new是一个隐式线程同步

是的,你听到了。你的操作系统需要确保你的页表是一致的,因此调用new会导致你的线程获得一个隐式互斥锁。如果你一直从许多线程调用new,你实际上是在序列化你的线程(我用32个cpu做过这个,每个cpu都调用new来获得几百个字节,哎呦!那是一个需要调试的皇家p.i.t.a.。)

其余的,比如速度慢、碎片化、容易出错等,其他答案已经提到了。

我倾向于不同意使用new“too much”的观点。尽管最初的海报使用new with system classes有点可笑。(int *我;I = new int[9999];?真的吗?int [9999];这样就清楚多了。)我想这就是让评论者生气的地方。

When you're working with system objects, it's very rare that you'd need more than one reference to the exact same object. As long as the value is the same, that's all that matters. And system objects don't typically take up much space in memory. (one byte per character, in a string). And if they do, the libraries should be designed to take that memory management into account (if they're written well). In these cases, (all but one or two of the news in his code), new is practically pointless and only serves to introduce confusions and potential for bugs.

When you're working with your own classes/objects, however (e.g. the original poster's Line class), then you have to begin thinking about the issues like memory footprint, persistence of data, etc. yourself. At this point, allowing multiple references to the same value is invaluable - it allows for constructs like linked lists, dictionaries, and graphs, where multiple variables need to not only have the same value, but reference the exact same object in memory. However, the Line class doesn't have any of those requirements. So the original poster's code actually has absolutely no needs for new.

原因很复杂。

首先,c++不进行垃圾收集。因此,对于每一个new,必须有一个对应的delete。如果您没有将此删除,那么您就有内存泄漏。现在,对于这样一个简单的例子:

std::string *someString = new std::string(...);
//Do stuff
delete someString;

这很简单。但是如果“Do stuff”抛出异常会发生什么?哎呀:内存泄漏。如果“做事情”问题提前回归会发生什么?哎呀:内存泄漏。

这是最简单的情况。如果你碰巧将这个字符串返回给某人,现在他们必须删除它。如果他们把它作为参数传递,接收它的人需要删除它吗?什么时候删除?

或者,你可以这样做:

std::string someString(...);
//Do stuff

没有删除。对象是在“堆栈”上创建的,一旦超出作用域就会被销毁。您甚至可以返回对象,从而将其内容传递给调用函数。你可以将对象传递给函数(通常作为引用或const-reference: void SomeFunc(std::string &iCanModifyThis, const std::string &iCantModifyThis)。等等。

全部不需要new和delete。不存在谁拥有内存或谁负责删除它的问题。如果你有:

std::string someString(...);
std::string otherString;
otherString = someString;

可以理解为otherString拥有someString数据的副本。它不是指针;它是一个单独的对象。它们可能碰巧具有相同的内容,但你可以在不影响另一个的情况下更改其中一个:

someString += "More text.";
if(otherString == someString) { /*Will never get here */ }

明白了吗?