Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← C++ 编程 / C++ Programming

内存管理 / Memory Management

1. C++ 内存管理总览:先看完整地图 / C++ Memory Management Map

2. 程序内存布局:内存不是只有堆和栈 / Program Memory Layout

3. 栈与函数调用:局部变量为什么会消失 / Stack and Function Calls

4. 堆与动态分配:new 到底做了什么 / Heap and Dynamic Allocation

5. 对象生命周期:地址还在不代表对象还活着 / Object Lifetime

6. RAII 与所有权:让对象负责清理 / RAII and Ownership Design

7. 智能指针与所有权关系:谁拥有,谁只是看 / Smart Pointers and Ownership Graphs

8. 容器、迭代器与内存失效 / Containers, Iterators, and Invalidation

9. allocator 与 PMR:把分配策略抽出来 / Allocators and PMR

10. 内存错误与 Sanitizer:把崩溃变成证据 / Memory Errors and Sanitizers

本页目录

对象生命周期:地址还在不代表对象还活着 / Object Lifetime ​

生命周期是 C++ 内存管理里最容易被低估的概念。

很多错误不是“没有内存”,而是“那块内存上已经没有你以为的那个对象”。

1. 生命周期是什么 ​

对象生命周期回答:

text
这个对象从什么时候开始存在?
到什么时候结束存在?
在这段时间内能不能按它的类型合法访问?
1
2
3

看代码:

cpp
void f() {
    int x = 42;
}
1
2
3

x 的生命周期大致是:

text
进入声明位置
  |
  v
构造或初始化 x
  |
  v
x 可以被合法使用
  |
  v
离开作用域
  |
  v
x 生命周期结束
1
2
3
4
5
6
7
8
9
10
11
12
13

对于内置类型,构造和析构不明显。

对于类类型,会更清楚:

cpp
#include <iostream>

struct Trace {
    Trace() { std::cout << "construct\n"; }
    ~Trace() { std::cout << "destroy\n"; }
};

void f() {
    Trace t;
}
1
2
3
4
5
6
7
8
9
10

运行 f() 会看到构造和析构的顺序。

2. 作用域、存储期、生命周期不要混在一起 ​

三个词要分开:

text
scope
  名字在哪里能被使用

storage duration
  对象所占存储能维持多久

lifetime
  对象本身什么时候开始和结束
1
2
3
4
5
6
7
8

例子:

cpp
int* p = nullptr;

{
    int x = 1;
    p = &x;
}
1
2
3
4
5
6

花括号结束后:

text
x 这个名字不可见
x 对象生命周期结束
p 这个变量仍然活着
p 保存的地址没有自动清零
1
2
3
4

于是 *p 是错误的。

3. 临时对象和引用 ​

看代码:

cpp
#include <string>

std::string make_name() {
    return "Alice";
}

int main() {
    const std::string& ref = make_name();
}
1
2
3
4
5
6
7
8
9

这里 make_name() 返回一个临时结果。

绑定到 const std::string& ref 时,临时对象的生命周期会延长到 ref 的生命周期结束。

但不要把这个规则扩大化。

下面就危险:

cpp
#include <string>

const std::string& bad() {
    return std::string("Alice");
}
1
2
3
4
5

函数返回时,临时对象不能被这样安全地延长给调用者。

调用者拿到的是悬垂引用。

4. string_view 不拥有字符 ​

std::string_view 经常让人踩坑。

cpp
#include <string>
#include <string_view>

std::string_view title() {
    std::string s = "C++";
    return s;
}
1
2
3
4
5
6
7

s 拥有字符。

string_view 只是观察字符。

text
inside title()

s object
  |
  v
characters: C + +
  ^
  |
string_view

after title() returns

s destroyed
characters no longer owned by living string
string_view still stores old address and length
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

所以:

text
view 不延长被观察对象的生命周期。
1

同类概念还有 std::span<T>。

它观察一段连续对象,但不拥有那段对象。

5. 容器中的引用也会失效 ​

看代码:

cpp
#include <vector>

int main() {
    std::vector<int> values{1, 2, 3};
    int& first = values[0];

    values.push_back(4);
    values.push_back(5);

    first = 10; // 可能危险
}
1
2
3
4
5
6
7
8
9
10
11

如果 push_back 导致 vector 重新分配缓冲区,旧元素会被搬到新位置,旧缓冲区释放。

first 仍然是一个引用,但它可能引用旧缓冲区里的位置。

所以对象生命周期还要和容器的存储变化一起看。

6. moved-from 对象仍然活着 ​

移动语义容易造成另一个误解:

cpp
#include <string>
#include <utility>

std::string s = "hello";
std::string t = std::move(s);
1
2
3
4
5

移动后,s 没有死。

s 仍然是一个有效对象。

但它的值处于“有效但未指定”的状态。

你可以:

cpp
s = "new value";
1

不要依赖:

cpp
if (s.empty()) { }
1

有些实现中它可能为空,但标准不要求你依赖这个细节。

这里说明:

text
生命周期结束
  对象不能再按原类型使用

moved-from
  对象仍然活着,但资源可能已被转移
1
2
3
4
5

7. 本篇总结 ​

生命周期判断顺序:

text
对象在哪里被构造?
谁拥有它?
哪个作用域或 owner 会结束它?
我手里的指针、引用、view 是否可能活得更久?
容器是否可能搬走它的存储?
移动后对象是否仍然有效?
1
2
3
4
5
6

C++ 内存管理最核心的能力之一,就是不被“地址还在”迷惑。

真正要问的是:

text
这个地址上是否仍然有一个活着的、类型正确的对象?
1

最后更新于:

Pager
上一篇4. 堆与动态分配:new 到底做了什么 / Heap and Dynamic Allocation
下一篇6. RAII 与所有权:让对象负责清理 / RAII and Ownership Design

持续记录,持续成长

Copyright © Tidenflow