C++11智能指针详细解析及面试题
一、原始指针常见的内存问题
手动管理原始指针时,极易出现以下 6 类致命问题,也是智能指针要解决的核心痛点:
- 资源释放了,指针本身未置空
- 野指针:指向未初始化内存,或只有一个指针指向已被释放的资源
- 悬垂指针:多个指针指向同一个已被释放的资源,其中一个释放后其余全部失效
- 踩内存:指针指向的内存已被其他数据覆盖,访问时会读到错误数据或破坏正常数据
- 双重释放:多次调用
delete释放同一个指针指向的地址,会破坏堆内存管理结构 - 未释放资源:忘记调用
delete,导致内存持续占用,长期运行会耗尽系统内存
二、智能指针核心原理
一句话总述:智能指针是 C++11 引入的、封装原始指针的模板类(位于<memory>头文件),严格遵循 **RAII(资源获取即初始化)** 思想,利用对象的生命周期自动管理动态资源的释放,无需手动调用delete,从根本上避免上述所有内存问题。C++ 标准库提供了三种核心智能指针,分工明确:std::unique_ptr(独占所有权)、std::shared_ptr(引用计数共享所有权)和std::weak_ptr(辅助shared_ptr解决循环引用)。
- RAII 核心逻辑:利用对象的生命周期绑定程序资源的生命周期
- 智能指针的实现基础:在构造函数中获取并接管资源,在析构函数中自动释放资源
- 额外优势:重载了
operator*和operator->,提供与原始指针几乎一致的使用体验
三、三种智能指针详解
1. std::shared_ptr —— 共享所有权
核心一句话:std::shared_ptr实现共享所有权语义,通过底层引用计数机制允许多个指针共同管理同一资源,核心解决悬垂指针和资源跨作用域共享的问题。
语义:共享所有权
- 资源没有明确的单一所有者,可能被多个对象、函数或线程同时操作
- 资源的生命周期由所有指向它的
shared_ptr共同决定
原理:使用引用计数机制
- 每新增一个
shared_ptr指向资源,引用计数 + 1 - 每销毁一个
shared_ptr(离开作用域或被重置),引用计数 - 1 - 只有当引用计数归 0时,才会自动调用析构函数释放资源
- 底层维护独立的控制块:存储强引用计数、弱引用计数、自定义删除器和指向资源的原始指针
核心使用场景:
-
在容器中管理指针
class T{}; // 原始写法:clear()只释放栈上的指针数组,堆上的T对象未释放,必须手动遍历delete vector<T*> vec; // 智能指针写法:调用clear()时,会自动析构所有T对象,无需手动处理 vector<shared_ptr<T>> vec; -
资源跨函数传递
// 原始写法:func中可能因逻辑分支遗漏、抛出异常等情况未执行delete,造成内存泄漏 { int *p = new int; func(p); } // 智能指针写法:shared_ptr生命周期结束时自动释放资源,异常安全 { auto p = make_shared<int>(); func(p); }
严格使用规范:使用shared_ptr管理动态资源时,绝对不要混用原始裸指针
-
构造智能指针时,不要暴露裸指针
// ❌ 不推荐:裸指针p被暴露,可能被外部意外delete,导致智能指针析构时重复释放 int *p = new int(); shared_ptr<int> sp = shared_ptr<int>(p); // ✅ 较好:隐藏了裸指针,避免外部误操作 shared_ptr<int> sp = shared_ptr<int>(new int); // ✅ 最优:使用make_shared构造(一次性分配对象和控制块内存,更安全高效,自动维护弱引用计数) shared_ptr<int> sp = make_shared<int>();补充:用裸指针构造
shared_ptr时,对象和控制块是分开分配的,且不会自动维护弱引用计数;make_shared则一次性完成所有分配,性能和安全性更优。 -
不要使用
get()接口获取裸指针int* p = sp.get(); // ❌ 绝对禁止:手动操作p会导致重复释放、踩内存、野指针等问题 -
不要由同一个裸指针构造多个智能指针对象
- 本质是多个独立的智能指针控制块管理同一块资源,会导致重复析构
- 常见错误:在类中用
this直接构造shared_ptr返回自身// ❌ 错误写法 class T{ public: shared_ptr<T> self(){ return shared_ptr<T>(this); } }; // ✅ 正确写法:继承enable_shared_from_this模板类,调用shared_from_this()返回 class T:public enable_shared_from_this<T>{ public: shared_ptr<T> self(){ return shared_from_this(); } };
2. std::unique_ptr —— 独占所有权
核心一句话:std::unique_ptr实现独占所有权语义,同一时刻仅一个指针拥有资源的所有权,禁止拷贝仅支持移动,核心解决野指针和重复释放问题。
语义:独享所有权
- 资源有固定且唯一的拥有者,不希望有多个指针同时指向和操作它
- 所有权可以转移,但转移后原指针立即失效
特点:
- 拷贝构造函数和赋值运算符被显式
delete,禁止拷贝 - 仅提供移动构造和移动赋值,通过
std::move转移所有权 - 无需维护引用计数,性能几乎与原始指针一致,开销极小
使用场景:资源的所有权明确且唯一的场景(如工厂函数返回值、局部动态对象、容器内的非共享对象)
使用规范:
- 不支持拷贝,但是可以从函数中返回一个
unique_ptrclass T1{}; unique_ptr<T1> get_unique(){ unique_ptr<T1> up = make_unique<T1>(); return up; } // 调用 unique_ptr<T1> up = get_unique();补充:编译器会优先进行返回值优化(RVO/NRVO),直接在调用方构造对象;如果关闭编译器优化,会按以下优先级调用:1. 移动构造 2. 拷贝构造 3. 没有拷贝构造则编译报错
- 推荐使用
std::make_unique构造(C++14 引入,C++11 需自行实现)
3. std::weak_ptr —— 弱引用
核心一句话:std::weak_ptr是不拥有资源所有权的弱引用指针,不会增加强引用计数,仅作为 "观察者" 存在,专门辅助shared_ptr解决循环引用导致的内存泄漏问题。
循环引用问题示例:
class B;
class A{
public:
~A(){
cout<<"A::~A()"<<endl;
}
shared_ptr<B> spb; // A持有B的强引用
};
class B{
public:
~B(){
cout<<"B::~B()"<<endl;
}
shared_ptr<A> spa; // B持有A的强引用
};
void func(){
shared_ptr<A> sp1 = make_shared<A>();
shared_ptr<B> sp2 = make_shared<B>();
sp1->spb = sp2; // sp2引用计数变为2
sp2->spa = sp1; // sp1引用计数变为2
}
- 问题:离开
func作用域后,sp1和sp2被销毁,两者的引用计数都减为 1,永远无法归 0,导致 A 和 B 的资源永远无法释放,造成内存泄漏 - 解决:将其中一方的强引用改为
weak_ptr,弱引用不占用强引用计数,从而打破循环引用链
核心特点:
- 不拥有资源所有权,不能直接解引用访问资源
- 仅能由
shared_ptr或另一个weak_ptr构造 - 可通过
expired()方法检查资源是否已被释放 - 可通过
lock()方法获取一个临时的shared_ptr(若资源未释放),用于安全访问资源
四、智能指针补充说明与使用要点
其他相关智能指针
std::auto_ptr<T>(C++98,已弃用):具有浅拷贝语义,拷贝时会自动转移所有权,极易导致悬空指针问题,已在 C++11 被std::unique_ptr完全取代std::scoped_ptr(Boost 库):与std::unique_ptr类似,但不支持移动语义,仅能在封闭作用域内使用
通用使用要点
- 避免裸指针混用:资源一旦交给智能指针管理,就不要再通过裸指针操作它
- 按需选择类型:优先使用
unique_ptr(性能最高);只有需要共享资源时才用shared_ptr;存在循环引用风险时必须搭配weak_ptr - 自定义删除器:
unique_ptr和shared_ptr都支持传入自定义删除器(函数对象、lambda 表达式等),可用于管理文件句柄、网络连接、数组等非new分配的资源 - 性能考虑:
unique_ptr无额外开销,适合高性能场景;shared_ptr的引用计数操作是原子的,有一定的线程安全开销,慎用在性能敏感的高并发场景
五、完整示例代码
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
struct Resource {
Resource(int v) : value(v) {
cout << "构造 Resource, value = " << value << endl;
}
~Resource() {
cout << "析构 Resource, value = " << value << endl;
}
int value;
};
int main() {
cout << "== unique_ptr 示例 ==\n";
{
// 创建 unique_ptr,通过 make_unique 更安全
auto up = make_unique<Resource>(10);
cout << "up->value = " << up->value << endl;
// 无法复制,只能移动所有权
// auto up2 = up; // 编译错误
auto up2 = move(up);
if (!up) {
cout << "up 已为空,所有权转移给 up2" << endl;
}
// up2 离开作用域后自动删除 Resource
}
cout << "\n== shared_ptr & weak_ptr 示例 ==\n";
{
// 创建 shared_ptr,通过 make_shared 更高效
auto sp1 = make_shared<Resource>(20);
cout << "sp1.use_count() = " << sp1.use_count() << endl;
{
auto sp2 = sp1; // 共享所有权,引用计数加 1
cout << "sp1.use_count() = " << sp1.use_count() << endl;
// 创建 weak_ptr,不增加引用计数
weak_ptr<Resource> wp = sp1;
cout << "wp.expired()? " << boolalpha << wp.expired() << endl;
if (auto temp = wp.lock()) {
cout << "通过 weak_ptr 获得临时 shared_ptr, value = " << temp->value << endl;
}
// sp2 离开内层作用域后引用计数减 1
}
cout << "sp1.use_count() = " << sp1.use_count() << endl;
// sp1 离开作用域后,引用计数为 0,Resource 被析构
}
cout << "\n== shared_ptr 循环引用示例(使用 weak_ptr 解决) ==\n";
struct Node {
int id;
shared_ptr<Node> next;
weak_ptr<Node> prev; // 用 weak_ptr 避免循环引用
Node(int i) : id(i) {
cout << "构造 Node " << id << endl;
}
~Node() {
cout << "析构 Node " << id << endl;
}
};
{
auto n1 = make_shared<Node>(1);
auto n2 = make_shared<Node>(2);
n1->next = n2; // n1 持有 n2 的强引用
n2->prev = n1; // n2 持有 n1 的弱引用,不增加引用计数
// 离开作用域后,n2、n1 的引用计数都能归零,依次析构
}
cout << "程序结束\n";
return 0;
}
/*
运行结果:
== unique_ptr 示例 ==
构造 Resource, value = 10
up->value = 10
up 已为空,所有权转移给 up2
析构 Resource, value = 10
== shared_ptr & weak_ptr 示例 ==
构造 Resource, value = 20
sp1.use_count() = 1
sp1.use_count() = 2
wp.expired()? false
通过 weak_ptr 获得临时 shared_ptr, value = 20
sp1.use_count() = 1
析构 Resource, value = 20
== shared_ptr 循环引用示例(使用 weak_ptr 解决) ==
构造 Node 1
构造 Node 2
析构 Node 1
析构 Node 2
程序结束
*/
代码说明:
- unique_ptr 部分:展示了独占所有权、移动语义和自动析构特性
- shared_ptr & weak_ptr 部分:展示了引用计数的变化、weak_ptr 的观察和临时访问能力
- 循环引用部分:对比了使用 shared_ptr 和 weak_ptr 的差异,验证了 weak_ptr 解决循环引用的有效性
六、核心总结表
| 智能指针类型 | 所有权 | 引用计数 | 核心解决问题 | 典型使用场景 | 性能开销 |
|---|---|---|---|---|---|
unique_ptr |
独占 | 无 | 野指针、重复释放 | 单一所有权资源、工厂函数返回值 | 极低 |
shared_ptr |
共享 | 有 | 悬垂指针、资源跨作用域共享 | 多对象共享资源、容器管理指针 | 中等 |
weak_ptr |
无 | 无 | shared_ptr 循环引用 | 双向链表、父子对象互相引用 | 极低 |
其他问题
1. make_shared 相比直接用 new 构造 shared_ptr 的优缺点
- 优点(必答):
- 一次性分配对象和控制块内存,减少一次内存分配,性能更高
- 异常安全:如果构造函数抛出异常,不会导致内存泄漏
- 自动维护弱引用计数,避免
weak_ptr悬空的极端情况
- 缺点(加分项):
- 无法使用自定义删除器
- 大对象可能导致内存碎片
- 当所有
shared_ptr销毁但还有weak_ptr存在时,整个内存块(对象 + 控制块)不会被释放,直到最后一个weak_ptr销毁
2. shared_ptr 的线程安全问题
- 结论:
shared_ptr的引用计数操作是线程安全的(原子操作),但对象本身的读写不是线程安全的 - 举例:
- 多个线程同时拷贝同一个
shared_ptr是安全的(引用计数原子增减) - 多个线程同时修改同一个
shared_ptr指向的对象是不安全的(需要加锁) - 多个线程同时让同一个
shared_ptr指向不同的对象是不安全的
- 多个线程同时拷贝同一个
3. enable_shared_from_this 的实现原理
- 为什么不能直接用
this构造shared_ptr:会生成两个独立的控制块,导致重复析构 - 实现原理:
enable_shared_from_this内部有一个weak_ptr成员,当第一个shared_ptr构造时,会自动初始化这个weak_ptr shared_from_this()本质是从内部的weak_ptr调用lock()生成一个新的shared_ptr,共享同一个控制块
4. unique_ptr 如何支持数组
- C++11 起
unique_ptr支持数组特化:std::unique_ptr<int[]> arr(new int[10]); - 数组特化的
unique_ptr会自动调用delete[]释放资源 - 注意:
shared_ptr不支持数组特化(C++17 前),如果用shared_ptr管理数组,必须传入自定义删除器:std::shared_ptr<int> arr(new int[10], [](int* p){ delete[] p; });
5. weak_ptr 的 expired() 和 lock() 的区别
expired():检查资源是否已被释放,返回boollock():如果资源未释放,返回一个有效的shared_ptr;否则返回空的shared_ptr- 最佳实践:永远不要先调用
expired()再调用lock()(多线程下可能存在竞态条件),直接用lock():// ❌ 错误:expired()和lock()之间资源可能被释放 if (!wp.expired()) { auto sp = wp.lock(); // 操作sp } // ✅ 正确:原子操作 if (auto sp = wp.lock()) { // 操作sp }
6. 智能指针的自定义删除器
- 什么时候用:管理非
new分配的资源(文件句柄、socket、数据库连接、malloc 分配的内存) unique_ptr的删除器是类型的一部分:std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("test.txt", "r"), fclose);shared_ptr的删除器不是类型的一部分,更灵活:std::shared_ptr<FILE> fp(fopen("test.txt", "r"), fclose);
7. shared_ptr 的控制块里到底有什么
- 强引用计数(
use_count()) - 弱引用计数(
weak_count()) - 自定义删除器(如果有)
- 指向资源的原始指针
- 对齐填充(为了内存对齐)
8. 什么是「智能指针的类型转换」
static_pointer_cast:静态转换,对应static_castdynamic_pointer_cast:动态转换,对应dynamic_cast(用于多态类型)const_pointer_cast:常量转换,对应const_cast- 注意:不能用普通的
static_cast直接转换智能指针,必须用上述专用函数
常见面试题
C++ 智能指针有哪些?它们的区别是什么?
C++ 主要有 3 种智能指针(位于<memory>头文件中):
-
std::unique_ptr<T>(独占所有权)- 不能被复制,只能移动(
std::move)。 - 适用于独占资源管理(如文件、网络连接)。
- 用
std::make_unique<T>(args...)创建(C++14+)。
- 不能被复制,只能移动(
-
std::shared_ptr<T>(共享所有权)- 采用引用计数,多个
shared_ptr可共享同一对象,最后一个销毁时释放资源。 - 存在循环引用风险,可配合
std::weak_ptr解决。 - 用
std::make_shared<T>(args...)创建,减少内存分配开销。
- 采用引用计数,多个
-
std::weak_ptr<T>(弱引用)- 依赖
shared_ptr,不会增加引用计数。 - 用于解决
shared_ptr循环引用问题。 - 可通过
lock()获取shared_ptr,判断对象是否仍然有效。
- 依赖
手写shared_ptr
一、核心原理
手写shared_ptr的本质是:用两个指针实现 "共享所有权"
- 一个指针
T* ptr:直接指向你要管理的堆对象(方便快速解引用) - 另一个指针
ShareCount<T>* countPtr:指向一个堆上的共享计数器对象 - 所有指向同一个堆对象的
shared_ptr,都共享同一个计数器 - 计数器记录当前有多少个
shared_ptr指向这个对象:- 新增一个持有者 → 计数 + 1
- 一个持有者销毁 / 重置 → 计数 - 1
- 计数减到 0 → 自动释放堆对象 + 释放计数器本身
二、逐模块代码解析
1. ShareCount 类:专门管引用计数和资源释放(最核心的底层)
这是整个实现的灵魂,所有的计数逻辑和资源释放都封装在这里,对外不可见。
template<typename T>
class ShareCount {
private:
T* ptr; // 指向真正的堆对象
int count; // 引用计数:记录有多少个shared_ptr指向它
// 禁止拷贝和赋值:计数器绝对不能被拷贝,必须全局唯一
ShareCount(const ShareCount&) = delete;
ShareCount& operator=(const ShareCount&) = delete;
public:
// 构造函数:第一个shared_ptr创建时,计数初始化为1
ShareCount(T* p) : ptr(p), count(1) {}
// 析构函数:真正释放堆对象的地方
~ShareCount() { delete ptr; }
// 新增一个持有者:计数+1
void increment() { count++; }
// 减少一个持有者:计数-1,到0就销毁自己和堆对象
void decrement() {
count--;
if (count == 0) {
delete this; // 先调用自己的析构函数(释放堆对象),再释放计数器本身
}
}
T* get() const { return ptr; }
};
2. shared_ptr 类:对外的智能指针接口
封装了两个指针,提供和原始指针一样的使用体验(->、*),同时自动管理生命周期。
template<typename T>
class shared_ptr {
private:
T* ptr; // 指向堆对象
ShareCount<T>* countPtr; // 指向共享计数器
public:
// -------------------------- 构造函数 --------------------------
// 普通构造:传入堆上的原始指针,创建计数器
shared_ptr(T* p = nullptr) : ptr(p), countPtr(nullptr) {
if (p) {
countPtr = new ShareCount<T>(p);
}
}
// 拷贝构造:多个shared_ptr共享同一个对象和计数器
shared_ptr(const shared_ptr& other) : ptr(other.ptr), countPtr(other.countPtr) {
if (countPtr) {
countPtr->increment(); // 计数+1
}
}
// 移动构造:转移所有权,原对象变成空指针(计数不变)
shared_ptr(shared_ptr&& other) noexcept : ptr(other.ptr), countPtr(other.countPtr) {
other.ptr = nullptr;
other.countPtr = nullptr;
}
// -------------------------- 析构函数 --------------------------
~shared_ptr() {
if (countPtr) {
countPtr->decrement(); // 计数-1,到0自动释放资源
}
}
// -------------------------- 运算符重载 --------------------------
// 让智能指针对象能像原始指针一样用->
T* operator->() const {
return ptr;
}
// 让智能指针对象能像原始指针一样用*解引用
T& operator*() const {
return *ptr;
}
// -------------------------- 核心方法 --------------------------
// reset:重置当前智能指针,指向新对象(或空)
void reset(T* p = nullptr) {
if (p != ptr) { // 避免自己reset自己
// 第一步:先释放原来的引用
if (countPtr) {
countPtr->decrement();
}
// 第二步:指向新的对象
ptr = p;
if (p) {
countPtr = new ShareCount<T>(p); // 新对象创建新计数器
} else {
countPtr = nullptr;
}
}
}
// get:获取原始指针(和C接口兼容)
T* get() const {
return ptr;
}
// -------------------------- 赋值运算符(必须补全) --------------------------
// 拷贝赋值
shared_ptr& operator=(const shared_ptr& other) {
if (this != &other) { // 防止自赋值
if (countPtr) countPtr->decrement(); // 释放当前资源
ptr = other.ptr;
countPtr = other.countPtr;
if (countPtr) countPtr->increment(); // 计数+1
}
return *this;
}
// 移动赋值
shared_ptr& operator=(shared_ptr&& other) noexcept {
if (this != &other) {
if (countPtr) countPtr->decrement(); // 释放当前资源
ptr = other.ptr;
countPtr = other.countPtr;
other.ptr = nullptr;
other.countPtr = nullptr;
}
return *this;
}
};
三、结合 main 函数走完整流程
我们一步步看main 函数执行时,内存和计数的变化:
int main() {
// 1. 创建ptr1,指向new int(10)
shared_ptr<int> ptr1(new int(10));
// 内存状态:
// ptr1.ptr = 0x1000(指向int值10)
// ptr1.countPtr = 0x2000(指向ShareCount对象)
// ShareCount(0x2000):ptr=0x1000, count=1
// 2. 拷贝构造ptr2,和ptr1共享同一个对象
shared_ptr<int> ptr2 = ptr1;
// 调用拷贝构造:
// ptr2.ptr = 0x1000,ptr2.countPtr = 0x2000
// countPtr->increment() → count=2
// 输出都是10,因为指向同一个对象
std::cout << "ptr1 :" << *ptr1 << std::endl; // 10
std::cout << "ptr2 :" << *ptr2 << std::endl; // 10
// 3. ptr1调用reset(),放弃所有权
ptr1.reset();
// 调用reset:
// countPtr->decrement() → count=1
// ptr1.ptr = nullptr,ptr1.countPtr = nullptr
// 注意:ptr2还持有所有权,所以int对象和计数器都没被释放
// ptr2仍然能正常访问
std::cout << "ptr2 :" << *ptr2 << std::endl; // 10
// 4. 移动构造ptr3,把ptr2的所有权转移给ptr3
shared_ptr<int> ptr3 = std::move(ptr2);
// 调用移动构造:
// ptr3.ptr = 0x1000,ptr3.countPtr = 0x2000
// ptr2.ptr = nullptr,ptr2.countPtr = nullptr
// 计数还是1(只是换了个持有者,总数没变)
// ptr3正常访问
std::cout << "ptr3 :" << *ptr3.get() << std::endl; // 10
// 5. main函数结束,所有局部变量按创建顺序逆序销毁
// 销毁ptr3:调用~shared_ptr() → countPtr->decrement() → count=0
// 触发ShareCount的decrement:
// 先调用~ShareCount() → delete 0x1000(释放int 10)
// 再delete this → 释放0x2000(释放计数器)
// 销毁ptr2:countPtr是nullptr,什么都不做
// 销毁ptr1:countPtr是nullptr,什么都不做
// ✅ 所有内存都被正确释放,没有泄漏!
}
极简面试版
template <typename T> // 模板,支持int、string、自定义类等任意类型
class shared_ptr {
private:
T* _ptr; // 1. 指向真正的堆对象(比如你new出来的int、点云、图像)
int* _count; // 2. 指向共享的引用计数(最核心的设计!)
public:
// 普通构造:创建第一个智能指针
shared_ptr(T* p) : _ptr(p), _count(new int(1)) {}
// 拷贝构造:多个智能指针共享同一个对象
shared_ptr(const shared_ptr& other) {
_ptr = other._ptr; // 大家指向同一个对象
_count = other._count; // 大家指向同一个计数
(*_count)++; // 多了一个持有者,计数+1
}
// 析构函数:智能指针离开作用域时自动调用
~shared_ptr() {
(*_count)--; // 我走了,计数-1
if (*_count == 0) { // 没人用了,彻底释放
delete _ptr; // 释放堆对象
delete _count; // 释放计数本身
}
}
// 让智能指针用起来和原始指针一模一样
T& operator*() { return *_ptr; } // 支持 *p 解引用
T* operator->() { return _ptr; } // 支持 p->xxx 访问成员
};
std::make_shared 相比std::shared_ptr<T>(new T(args...))有什么好处?
使用 std::make_shared<T>(args...) 相比 std::shared_ptr<T>(new T(args...)) 主要有以下几个好处:
-
避免额外的内存分配
std::make_shared会在一次内存分配中同时分配对象本体和引用计数,而std::shared_ptr<T>(new T(args...))需要两次分配(一次给 T,一次给 shared_ptr 的控制块)。- 这不仅减少了
malloc/free的开销,还能提高缓存命中率。
-
减少异常安全问题
std::shared_ptr<T>(new T(args...))是两个独立的操作,new T(args...)可能会抛出异常,而 shared_ptr 还未成功构造,导致内存泄漏。std::make_shared进行的是原子操作,不存在这个问题。
-
更高效的引用计数管理
- 由于
std::make_shared在一个内存块中存储对象和引用计数,指针访问时可以减少额外的缓存访问,提高运行效率。 std::shared_ptr<T>(new T(args...))由于分开分配对象和控制块,会导致额外的指针间接访问。
- 由于
-
代码更简洁
auto ptr = std::make_shared<T>(args...)比auto ptr = std::shared_ptr<T>(new T(args...))更简短,可读性更好。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)