问题

C++ 中,如果指针换了被指向的东西,那被指向的原来的东西(是被 new 出来的)所占的内存会立刻被释放吗?

回答
在 C++ 中,当你有一个指针,然后让这个指针指向了新的内存地址,而它原来指向的内存地址是通过 `new` 分配出来的,那么原来被指向的那个对象的内存并不会“立刻”被释放。C++ 的内存管理机制需要你主动去处理。

让我为你细致地讲讲这个过程,尽量去除那些生硬的、像 AI 才会用的表述。

想象一下,你有一个盒子,里面装着一个你用 `new` 创造出来的东西,比如一个 `new int(10)`。你还有一个标签,上面写着这个盒子的地址,这就是你的指针。

```c++
int ptr = new int(10); // 你现在有了一个指针 ptr,它指向一个 int 对象,值为 10
```

这个 `new int(10)` 就像是你在一个大型的仓库(堆内存)里找了一个空的储物格,然后把一个整数 `10` 塞了进去,并把这个储物格的编号(内存地址)记在了你的标签(指针 `ptr`)上。

现在,你想让这个标签指向另一个储物格。比如,你又用 `new` 创造了另一个东西,或者你有一个指向其他内存地址的指针,然后你把这个新的地址写在了 `ptr` 这个标签上:

```c++
ptr = new int(20); // 现在 ptr 指向了另一个 new 出来的 int 对象,值为 20
```

这里发生了什么?你的标签 `ptr` 上的地址被改写了。它现在指向的是堆内存里另一个储物格,里面装着整数 `20`。

那么,原来那个装着整数 `10` 的储物格呢?

它还在那里。仓库(堆内存)并没有因为你的标签(指针)不再指向它而自动把它清空。它依然被占用着,即使没有任何标签指向它,也无人能再通过 `ptr` 找到它。这块内存,我们就说它发生了内存泄漏。

为什么 C++ 不会自动释放?

C++ 的设计哲学之一是让你对内存有精细的控制。自动内存管理(垃圾回收)是某些其他语言(如 Java, Python)的特点,但 C++ 选择了一种更“手动”的方式。这样做的好处是,你可以精确地知道什么时候内存被分配,什么时候需要释放,从而优化性能,避免不必要的开销。坏处也很明显,如果你不小心,就容易忘记释放,导致内存泄漏。

那么,你该如何“释放”呢?

你需要明确地告诉 C++,你不再需要那块内存了。使用的关键字是 `delete`:

```c++
int ptr = new int(10);
// ... 一些操作 ...
delete ptr; // 这会释放 ptr 当前指向的内存
ptr = nullptr; // 这是一个好习惯,避免成为“野指针”
```

在这个例子中,`delete ptr;` 会找到 `ptr` 当前指向的那个内存地址,将其中的内容(整数 `10`)销毁,并把那个储物格还给仓库。然后,`ptr` 变成了一个“空标签”(`nullptr`),它不再指向任何有效的内存。

回到你的问题:“如果指针换了被指向的东西,那被指向的原来的东西(是被 new 出来的)所占的内存会立刻被释放吗?”

答案是:不会。

只有当你显式地对那个“原来的东西”执行了 `delete` 操作,并且那个“原来的东西”确实是通过 `new` 分配的(而不是栈上的变量或者其他方式创建的),它所占用的内存才会被回收。如果你改变了指针的指向,而没有先 `delete` 原来指向的那块内存,那么那块内存就永远丢失了,直到你的程序结束(操作系统可能会在程序结束后回收所有未释放的内存,但这仍然是泄漏)。

所以,关键在于:指针只是一个指向内存地址的“标签”,改变这个标签的指向,并不会影响它原来指向的内存的实际状态。内存的释放,必须由你主动发起。

如果那个“被 new 出来的东西”是一个对象(而不仅仅是基础类型如 `int`),`delete` 在释放内存之前,还会调用这个对象的析构函数(destructor),执行一些清理工作。这比简单地还回一个储物格要复杂一些,但核心的内存释放原则依然不变:需要显式 `delete`。

网友意见

user avatar
如果不能,原来//new出来的东西就永远也不能再delete掉了吗?

正确,这就叫做“内存泄漏”。

当然,标准的解法是使用 std::shared_ptr 之类,但那就是另一个问题了。

类似的话题

  • 回答
    在 C++ 中,当你有一个指针,然后让这个指针指向了新的内存地址,而它原来指向的内存地址是通过 `new` 分配出来的,那么原来被指向的那个对象的内存并不会“立刻”被释放。C++ 的内存管理机制需要你主动去处理。让我为你细致地讲讲这个过程,尽量去除那些生硬的、像 AI 才会用的表述。想象一下,你有一.............
  • 回答
    在 C 语言中,`sizeof()` 操作符的魔法之处在于它能够根据其操作数的类型和大小来返回一个数值。而对于数组名和指针,它们虽然在某些上下文中表现得相似(例如,在函数参数传递时),但在 `sizeof()` 的眼中,它们的身份是截然不同的。这其中的关键在于数组名在绝大多数情况下会发生“衰减”(d.............
  • 回答
    在C++里,谈到“堆区开辟的属性”,咱们得先明白这指的是什么。简单来说,就是程序在运行的时候,动态地在内存的一个叫做“堆”(Heap)的地方分配了一块空间,用来存放某个对象或者数据。这块内存不像那些直接定义在类里的成员变量那样,跟随着对象的生命周期一起被自动管理。堆上的内存,需要我们手动去申请(比如.............
  • 回答
    在 C/C++ 项目中,将函数的声明和实现(也就是函数体)直接写在同一个头文件里,看似方便快捷,实际上隐藏着不少潜在的麻烦。这种做法就像是把家里的厨房和卧室直接打通,虽然一开始可能觉得省事,但长远来看,带来的问题会远超于那一点点便利。首先,最直接也是最普遍的问题是 重复定义错误 (Multiple .............
  • 回答
    在一个神经网络的选特征环节,如果一个特征(我们称之为特征 C)在算术意义上可以被表示为另外两个特征(特征 A 和特征 B)的和,即 C = A + B,那么是否还有必要选择特征 C,这是一个非常值得探讨的问题,而且答案并不是绝对的“是”或“否”,需要根据具体情况来分析。从理论上讲,如果 C = A .............
  • 回答
    这个问题很有趣,因为它触及了核物理学和有机化学的交叉点。答案是肯定的,这个环确实会变成氮杂环。不过,事情并非这么简单,其中的过程和影响值得我们细细道来。首先,我们需要理解一个基本概念:同位素衰变。你提到的C14是一个放射性同位素,它会通过一种叫做β衰变的过程发生转化。β衰变是指原子核中的一个中子转变.............
  • 回答
    这个问题,就像问是在崎岖的山路上徒步,还是在平坦的公路开车,各有各的精彩,也各有各的挑战。C++ 和 Java,这两位编程界的“巨头”,各有千秋,选择哪一个,完全取决于你的目的地和对旅途的要求。咱们先从 C++ 说起,这位老兄,绝对是编程界的“老炮儿”。C++:力量与控制的艺术如果你想要的是极致的性.............
  • 回答
    在 C++ 中,将 `std::string` 类型转换为 `int` 类型有几种常见且强大的方法。理解它们的原理和适用场景对于编写健壮的代码至关重要。下面我将详细介绍几种常用的方法,并分析它们的优缺点: 方法一:使用 `std::stoi` (C++11 及以后版本)这是 最推荐 的方法,因为它提.............
  • 回答
    在 C 中,如果你有一个对象的某个字段,并且这个字段的类型是 `Dictionary`,你想通过反射来获取这个字典的所有值,这完全是可行的。下面我将详细说明如何做到这一点,力求让整个过程清晰易懂,并且不像机器生成的教程那样生硬。想象一下,我们有一个类,里面有一个字段,这个字段恰好是一个字典。我们的目.............
  • 回答
    在 C 中,构建一个按照顺序执行的任务集合,而无需 `async` 和 `await` 关键字,这其实是通过巧妙地利用 `Task` 对象的链式调用来实现的。虽然 `async/await` 是目前处理这类问题的最直观和推荐的方式,但在某些特定场景下,或者为了理解底层的任务调度机制,我们也可以回归到.............
  • 回答
    在 C 中,内存管理是一个关键但又常常被误解的领域。虽然 .NET 运行时(CLR)负责大部分的内存回收工作,但作为开发者,我们仍然可以通过一些明智的实践来确保应用程序高效地运行,避免内存泄漏和不必要的开销。 了解垃圾回收(GC)首先,要有效地管理内存,我们就必须理解 C 的垃圾回收机制。想象一下,.............
  • 回答
    在 C 中与 Native DLL 进行线程间通信,尤其是在 Native DLL 内部创建了新的线程,这确实是一个比较考验功力的问题。我们通常不是直接“命令” Native DLL 中的某个线程与 C 中的某个线程通信,而是通过一套约定好的机制,让双方都能感知到对方的存在和传递的数据。这里我们不谈.............
  • 回答
    在 C 中实现 Go 语言 `select` 模式的精髓,即 等待多个异步操作中的任何一个完成,并对其进行处理,最贴切的类比就是使用 `Task` 的组合操作,尤其是 `Task.WhenAny`。Go 的 `select` 语句允许你监听多个通道(channel)的状态,当其中任何一个通道有数据可.............
  • 回答
    在 C 中,当一个泛型基类 `Base` 被设计成允许子类自身作为类型参数来继承时,例如 `class A : Base`,这是一种非常有趣且强大的模式,但同时也伴随着一些需要仔细考虑的约定和潜在的陷阱。这种模式通常被称为“递归泛型”或“自我引用泛型”。核心理念:这种设计模式的核心在于,子类 `A`.............
  • 回答
    在C中确实不存在Java或C++那样的“友元类”(friend class)机制。这常常让习惯了这种特性的开发者感到不适应,甚至认为这种设计“不太合理”。但实际上,C的设计哲学侧重于封装和明确的接口,友元类这种打破封装的特性并非是其追求的目标。那么,这种设计真的“不合理”吗?或者说,我们是否可以找到.............
  • 回答
    在C的世界里,Expression Trees(表达式树)确实是一个值得深入钻研的领域。它不像 LINQ 的基本查询语法那样是日常编码的必备工具,但一旦你触及到需要动态生成、修改代码,或者需要更底层地控制代码执行的场景,Expression Trees 的价值就会显现出来。是否需要学习?答案是:看你.............
  • 回答
    咱们聊聊 C 里的接口,这玩意儿在实际开发中,那可是个顶顶重要的角色,但要是光看定义,可能觉得有点抽象。我试着把这些实际用法给你掰开了揉碎了讲讲,尽量避免那些“AI味儿”的说法,就跟咱们哥俩坐一块儿聊天一样。接口是啥?通俗点说,就是一份“合同”你可以把接口想象成一个约定,或者一份“合同”。这份合同规.............
  • 回答
    const 的守护之剑:编译器如何雕琢 C/C++ 中的不变之道在C/C++的世界里,`const` 并非只是一个简单的关键字,它更像一把锋利的守护之剑,承诺着数据的不可变性,为程序的稳定性和可维护性筑起一道坚实的壁垒。那么,这把剑究竟是如何被铸造和挥舞的呢?这背后,是编译器一系列精巧的设计和严密的.............
  • 回答
    在 C 语言的世界里,“字符串常量”这个概念,说起来简单,但仔细品味,却能发现不少门道。它不像那些需要你绞尽脑汁去理解的复杂算法,但如果你对它不够了解,很容易在一些细节上栽跟头,甚至造成意想不到的bug。所以,咱们就来掰扯掰扯,看看这个 C 语言里的“小明星”,到底是怎么回事。首先,它是个啥?最直观.............
  • 回答
    深入剖析 C++ 结构体的大小: byte 之间的奥秘在 C++ 的世界里,我们经常会遇到 `struct`,用来组织相关的数据成员。当我们说“结构体的大小”时,我们实际上是在讨论它在内存中占据的字节数。这个数字看似简单,但背后却牵扯到编译器的优化、内存对齐等一系列复杂的机制。本文将带你深入理解 C.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有