4

我是一名 C 和 Java 程序员,所以内存分配和 OOP 对我来说并不是什么新鲜事。但是,我不确定如何准确地避免 C++ 对象实现的内存泄漏。即:

string s1("0123456789");
string s2 = s1.substr(0,3);

s2现在有一个新的字符串对象,所以它必须通过以下方式释放:

delete &s2;

对?

此外,我是否正确地假设我必须删除函数返回的任何(新)对象的地址,而不管返回类型不是指针或引用?生活在堆上的对象在必须被释放时不会作为指针返回,这似乎很奇怪。

4

8 回答 8

16

不。

您只需要释放您分配的内存(即通过 new 或 memalloc)。

于 2009-06-28T21:05:01.297 回答
12

不,

当超出范围时,两者都会被s1破坏。s2

s1.substr()将创建一个您不必考虑的临时对象。

于 2009-06-28T21:01:33.723 回答
8

答案有几个层次。

首先,变量可以用几种不同的方式声明:

  • 作为局部变量(在函数内部或作为类成员)。示例:int iint i = 42。这些具有自动存储持续时间(这些示例在技术上是auto int i, 或的简写auto int i = 42,尽管该auto关键字实际上从未使用过。这意味着当这些变量超出范围时将自动释放它们。保证调用它们的析构函数(不无论你如何离开作用域,无论是通过函数返回还是抛出异常,它们的析构函数将在它们超出作用域时被调用,然后它们使用的内存被释放)。像这样声明的局部变量被分配在堆栈。
  • 一个静态变量(带有static关键字,暗示静态存储持续时间,而不是上面显示的自动。这些只是在程序的持续时间内保留,因此不必被释放
  • 在堆上,使用动态分配(new intnew int(42))。这些必须通过调用手动释放delete

所以在最低级别,你基本上只需要保持对称性。如果某些东西是用 分配的new,用delete,malloc is freed byfree , andnew[] bydelete[]` 释放它。并且没有任何这些声明的变量会自动处理,不应手动释放。

现在,为了简化内存管理,通常使用RAII 。该技术基于以下观察:只有动态分配的对象必须手动释放,并且局部变量为您提供了一个非常方便的钩子,用于在局部变量超出范围时实现自定义操作。

因此,动态分配被封装在一个单独的类中,该类可以作为堆栈上的本地对象进行分配。delete然后,它可以在其构造函数中进行您需要的任何动态分配,并且其析构函数使用必要的调用对其进行清理。

这意味着您基本上不必调用delete您的顶级代码。它几乎总是隐藏在 RAII 对象的析构函数后面。new调用也变得很少,但仍然与智能指针一起使用(例如:boost::shared_ptr<int>(new int(42))它动态分配一个整数,然后将其传递给一个智能指针,该指针接管它的所有权,并自动清理它。

于 2009-06-28T22:20:25.997 回答
5

s1 和 s2 都是自动分配的。你不删除那些。您只delete使用您创建的对象new

C++ 知道三种分配模式:自动、静态和动态。阅读这些。自动值,例如示例中的字符串,在离开范围时会自动释放。他们的析构函数被自动调用。当调用字符串析构函数时,字符串在其操作期间动态分配的任何内存都会被释放。

于 2009-06-28T21:04:02.750 回答
3

就像其他人所说的那样 - 这里不需要删除。除非您看到 a new,否则您(通常)不需要 a delete。我确实想添加s2没有新的字符串对象。s2从它被声明的那一刻起就是一个字符串对象。分配一个 to 的切片s1进行s2修改s2,使其包含与子字符串中相同的字符。

了解这里发生的事情真的很重要,而且随着您对 C++ 的深入了解,它会变得更加重要。哦,恭喜你学习了一门新语言。

于 2009-06-28T21:07:47.797 回答
1

为什么必须手动释放 s2?s2 中动态内存的删除将由 std::string 析构函数处理。

于 2009-06-28T21:01:52.270 回答
1

不,s2不需要手动删除。它是堆栈上的一个局部变量,一旦超出范围就会自动销毁,就像它在声明时自动分配一样。一般来说,你只有delete你分配的东西new

函数返回的临时对象是自动管理的,并在语句结束时被销毁 - 如果需要更长的时间,它们通常会在临时对象被销毁之前复制到局部变量(例如,通过简单的赋值Object o = f();,如substr()打电话给你的例子)。

如果函数返回一个指针,则指向的对象不会像这样自动管理。在文档中应注明在所有工作完成后谁负责删除该对象。作为默认约定,分配对象的人通常负责再次删除它,但细节需要在某处记录。

于 2009-06-28T21:03:16.730 回答
1

在现代 C++(尤其是来自 ac 背景)中要理解的最重要的概念之一是RAII。C++ 将诸如内存(或互斥体或事务)之类的资源封装在类中,这些类在构造时“获取”资源(字符串构造函数分配动态内存)并在销毁时“释放”它(字符串类的销毁释放它)。由于堆栈对象的销毁是确定性的(基于堆栈的对象在封闭范围内过期),因此不必编写释放,即使抛出异常也会发生。

所以不行。在我的大部分编码中,我从未编写过显式删除(或 delete[]),因为资源要么由字符串、STL 容器或 shared_ptr 或 scoped_ptr 管理。

于 2009-06-28T21:21:57.083 回答