C++ 创建对象的几种方式与智能指针
C++ 创建对象的几种方式与智能指针详解
在 C++ 中,对象的创建方式多样,涵盖了栈、堆、静态存储区、placement new 以及现代 C++ 提供的智能指针机制。理解这些方式的区别与适用场景,是写出安全高效 C++ 程序的关键。
✅ 一、栈分配(Stack Allocation)
栈分配是在函数作用域内创建对象的最常见方式,生命周期受限于作用域,自动调用析构函数,适用于 RAII 模式。
class MyClass {
public:
MyClass() { std::cout << "构造\n"; }
~MyClass() { std::cout << "析构\n"; }
};
int main() {
MyClass obj; // 栈上分配
} // 离开作用域,obj 自动析构
✅ 高效安全,默认推荐方式。
✅ 二、堆分配(Heap Allocation)
使用 new
创建对象,需使用 delete
显式释放。
int main() {
int* p = new int(10);
std::cout << *p << std::endl;
delete p; // 否则内存泄漏
}
❌ 容易出现内存泄漏,不建议手动管理堆内存。
✅ 三、全局 / 静态存储区对象
全局变量或在函数中使用 static
修饰的对象,其生命周期为整个程序执行期间。
static int a = 10;
class MyClass {
public:
MyClass() { std::cout << "构造\n"; }
~MyClass() { std::cout << "析构\n"; }
};
static MyClass globalObj; // 全局静态对象,程序结束时析构
✅ 适用于缓存、单例、日志等长生命周期场景。
✅ 四、placement new
允许在用户提供的内存区域上构造对象,常用于内存池、自定义分配器。
#include <iostream>
class A {
public:
A(int x) : x(x) { std::cout << "A(int x)\n"; }
~A() { std::cout << "~A()\n"; }
int x;
};
int main() {
char buffer[sizeof(A)];
A* a = new (buffer) A(10); // 使用 placement new
std::cout << a->x << std::endl;
a->~A(); // 手动析构
}
注意事项:
- ✅ 不使用
delete
,需手动调用析构函数 - ✅ buffer 大小和对齐必须满足要求
- ✅ 避免对象重叠构造,适合底层场景
✅ 五、临时对象(匿名对象)
class Temp {
public:
Temp() { std::cout << "构造\n"; }
~Temp() { std::cout << "析构\n"; }
};
int main() {
Temp(); // 创建后立即析构
}
✅ 常用于语义清晰的短生命周期对象或副作用逻辑。
✅ 六、智能指针(Smart Pointer)
智能指针是 RAII 的现代实现形式,自动释放堆内存,避免手动 delete
带来的风险。
1. std::unique_ptr
- 独占所有权
#include <memory>
std::unique_ptr<int> p1 = std::make_unique<int>(10); // 推荐写法
- ✅ 不可拷贝,只能移动
- ✅ 自动释放资源,轻量高效
2. std::shared_ptr
- 引用计数共享所有权
#include <memory>
std::shared_ptr<int> p2 = std::make_shared<int>(20);
auto p3 = p2;
std::cout << "引用计数: " << p2.use_count() << std::endl;
- ✅ 自动管理资源,最后一个引用释放资源
- ✅ 可用于多个模块共享资源
3. std::weak_ptr
- 弱引用
std::weak_ptr<int> wp = p2;
if (auto sp = wp.lock()) {
std::cout << *sp << std::endl;
} else {
std::cout << "资源已释放" << std::endl;
}
- ✅ 不增加引用计数,用于观察 shared_ptr 管理的对象
- ✅ 避免循环引用
✅ 七、对象创建方式对比总结
创建方式 | 存储位置 | 生命周期 | 是否需手动释放 | 推荐程度 | 典型场景 |
---|---|---|---|---|---|
栈分配 | 栈 | 作用域结束 | 否 | ✅✅✅ | 局部变量、RAII |
堆分配 | 堆 | 手动控制 | 是(delete ) |
❌ | 老代码、教学演示 |
静态分配 | 数据段 | 程序全程 | 否 | ✅(特定) | 单例、缓存、日志 |
placement new | 手动区域 | 手动构造析构 | 是 | 高阶用法 | 内存池、容器、定制系统 |
临时对象 | 栈 | 表达式结束 | 否 | ✅ | 快速触发构造+析构 |
unique_ptr |
堆 | 所有权唯一 | 否 | ✅✅✅ | 推荐默认方式 |
shared_ptr |
堆 | 引用计数共享 | 否 | ✅✅ | 多模块共享 |
weak_ptr |
无资源 | 弱引用,不持有 | 否 | ✅ | 缓存、观察、避免循环引用 |