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 无资源 弱引用,不持有 缓存、观察、避免循环引用