C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ 智能指针使用

C++ 智能指针原理、使用与最佳实践指南

作者:2401_87727424

本文全面介绍了C++智能指针的使用,包括RAII思想、标准库实现、核心原理和实际问题解决,重点讲解了unique_ptr、shared_ptr和weak_ptr的使用场景、优缺点及特殊资源管理,感兴趣的朋友跟随小编一起看看吧

前言

在 C++ 编程中,动态内存管理是核心难点之一,手动new/delete容易因异常处理、逻辑疏忽导致内存泄漏。智能指针作为 RAII 思想的典型实现,完美解决了这一问题。本文将结合完整代码示例,从使用场景、设计思路、标准库实现、核心原理到实际问题解决,全面讲解智能指针的相关知识。

一、智能指针的核心使用场景

手动管理动态内存时,异常会打断程序执行流程,导致后续delete语句无法执行,最终造成内存泄漏。以下是典型教学代码:

double Divide(int a, int b) {
    if (b == 0)
        throw "Divide by zero condition!"; // 除0抛出异常
    else
        return (double)a / (double)b;
}
void Func() {
    int* array1 = new int[10];
    int* array2 = new int[10]; // 若此处抛异常,array1已无法释放
    try {
        int len, time;
        cin >> len >> time;
        cout << Divide(len, time) << endl;
    }
    catch (...) {
        // 捕获异常后释放资源,再重新抛出
        cout << "delete []" << array1 << endl;
        cout << "delete []" << array2 << endl;
        delete[] array1;
        delete[] array2;
        throw;
    }
    // 正常流程释放资源
    cout << "delete []" << array1 << endl;
    delete[] array1;
    cout << "delete []" << array2 << endl;
    delete[] array2;
}
int main() {
    try {
        Func();
    }
    catch (const char* errmsg) {
        cout << errmsg << endl;
    }
    catch (const exception& e) {
        cout << e.what() << endl;
    }
    catch (...) {
        cout << "未知异常" << endl;
    }
    return 0;
}

痛点分析:为覆盖所有异常场景,需要嵌套多层try-catch,代码冗余且易出错。

二、RAII 思想与智能指针设计思路

2.1 RAII 核心思想

RAII(Resource Acquisition Is Initialization)即 “资源获取即初始化”,本质是一种利用对象生命周期来管理获取到的动态资源,避免资源泄漏的资源管理机制。

2.2 智能指针的设计要点

智能指针需满足两个核心需求:遵循 RAII 思想、模拟原生指针行为。以下是简化的智能指针实现:

template<class T>
class SmartPtr {
public:
    // RAII:构造时接管资源
    SmartPtr(T* ptr) : _ptr(ptr) {}
    // 析构时自动释放资源
    ~SmartPtr() {
        cout << "delete[] " << _ptr << endl;
        delete[] _ptr;
    }
    // 重载运算符,模拟指针行为
    T& operator*() { return *_ptr; }
    T* operator->() { return _ptr; }
    T& operator[](size_t i) { return _ptr[i]; }
private:
    T* _ptr; // 管理的资源指针
};

2.3 优化后的 Func 函数

使用自定义SmartPtr后,无需手动释放资源,异常场景下也能自动析构:

double Divide(int a, int b) {
    if (b == 0)
        throw "Divide by zero condition!";
    else
        return (double)a / (double)b;
}
void Func() {
    // 智能指针管理动态数组,自动释放
    SmartPtr<int> sp1 = new int[10];
    SmartPtr<int> sp2 = new int[10];
    for (size_t i = 0; i < 10; i++)
        sp1[i] = sp2[i] = i; // 重载[]运算符,直接访问
    int len, time;
    cin >> len >> time;
    cout << Divide(len, time) << endl;
}
int main() {
    try {
        Func();
    }
    catch (const char* errmsg) {
        cout << errmsg << endl;
    }
    catch (const exception& e) {
        cout << e.what() << endl;
    }
    catch (...) {
        cout << "未知异常" << endl;
    }
    return 0;
}

三、C++ 标准库智能指针的使用

C++ 标准库提供的智能指针均定义在<memory>头文件中,核心包括auto_ptr(已废弃)、unique_ptrshared_ptrweak_ptr,它们的核心差异在于资源所有权管理机制。

3.1 各智能指针核心特性对比

智能指针推出标准核心特性适用场景
auto_ptrC++98拷贝时转移资源所有权已废弃,避免使用
unique_ptrC++11独占资源,禁止拷贝,支持移动无需共享资源的场景
shared_ptrC++11共享资源,引用计数实现需要拷贝 / 共享资源的场景
weak_ptrC++11不管理资源,不增加引用计数解决shared_ptr循环引用

3.2 基础使用示例

首先定义测试类Date,用于验证析构行为:

struct Date {
    int _year;
    int _month;
    int _day;
    Date(int year = 1, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {}
    ~Date() {
        cout << "~Date()" << endl;
    }
};

3.2.1auto_ptr(废弃)

auto_ptr的拷贝会转移资源所有权,导致原对象悬空,访问时会崩溃:

int main() {
    auto_ptr<Date> ap1(new Date);
    auto_ptr<Date> ap2(ap1); // 拷贝后,ap1的资源所有权转移给ap2
    // ap1->_year++; // 错误:ap1已悬空,空指针访问
    return 0;
}

3.2.2unique_ptr(独占所有权)

unique_ptr禁止拷贝,支持移动语义(移动后原对象悬空,需谨慎使用):

int main() {
    unique_ptr<Date> up1(new Date);
    // unique_ptr<Date> up2(up1); // 错误:禁止拷贝
    unique_ptr<Date> up3(move(up1)); // 支持移动,up1悬空
    return 0;
}

3.2.3shared_ptr(共享所有权)

shared_ptr通过引用计数跟踪资源持有者数量,拷贝时计数递增,析构时计数递减,计数为 0 时释放资源:

int main() {
    shared_ptr<Date> sp1(new Date);
    shared_ptr<Date> sp2(sp1); // 拷贝,引用计数变为2
    shared_ptr<Date> sp3(sp2); // 拷贝,引用计数变为3
    cout << sp1.use_count() << endl; // 输出3:查看引用计数
    sp1->_year++; // 重载->,直接访问成员
    cout << sp1->_year << endl; // 输出2
    cout << sp2->_year << endl; // 输出2(共享同一资源)
    cout << sp3->_year << endl; // 输出2
    shared_ptr<Date> sp4(move(sp1)); // 移动,sp1悬空
    return 0;
}

3.3 特殊资源管理:删除器

智能指针默认使用delete释放资源,若管理new[]分配的数组或文件句柄等特殊资源,需自定义删除器(可调用对象)。

3.3.1 管理new[]数组

unique_ptrshared_ptr提供了new[]特化版本,直接支持数组管理:

int main() {
    // 特化版本,析构时自动调用delete[]
    unique_ptr<Date[]> up1(new Date[5]);
    shared_ptr<Date[]> sp1(new Date[5]);
    return 0;
}

重点: 自定义删除器(三种方式)

此处提供仿函数、函数指针、lambda 表达式三种删除器实现:

// 1. 函数指针删除器
template<class T>
void DeleteArrayFunc(T* ptr) {
    delete[] ptr;
}
// 2. 仿函数删除器
template<class T>
class DeleteArray {
public:
    void operator()(T* ptr) {
        delete[] ptr;
    }
};
// 3. 文件句柄删除器(仿函数)
class Fclose {
public:
    void operator()(FILE* ptr) {
        cout << "fclose:" << ptr << endl;
        fclose(ptr);
    }
};
int main() {
    // 仿函数作为删除器(unique_ptr模板参数指定,shared_ptr构造函数传入)
    unique_ptr<Date, DeleteArray<Date>> up2(new Date[5]);
    shared_ptr<Date> sp2(new Date[5], DeleteArray<Date>());
    // 函数指针作为删除器
    unique_ptr<Date, void(*)(Date*)> up3(new Date[5], DeleteArrayFunc<Date>);
    shared_ptr<Date> sp3(new Date[5], DeleteArrayFunc<Date>);
    // lambda表达式作为删除器
    auto delArrOBJ = [](Date* ptr) { delete[] ptr; };
    unique_ptr<Date, decltype(delArrOBJ)> up4(new Date[5], delArrOBJ);
    shared_ptr<Date> sp4(new Date[5], delArrOBJ);
    // 管理文件句柄
    shared_ptr<FILE> sp5(fopen("Test.cpp", "r"), Fclose());
    shared_ptr<FILE> sp6(fopen("Test.cpp", "r"), [](FILE* ptr) {
        cout << "fclose:" << ptr << endl;
        fclose(ptr);
    });
    return 0;
}

3.4 其他实用特性

make_shared构造:直接通过参数初始化资源,更高效(减少一次内存分配):

int main() {
    shared_ptr<Date> sp2 = make_shared<Date>(2024, 9, 11); // 直接初始化日期
    auto sp3 = make_shared<Date>(2024, 9, 11); // 自动推导类型
    return 0;
}

operator bool类型转换:直接判断智能指针是否管理资源:

int main() {
    shared_ptr<Date> sp1(new Date(2024, 9, 11));
    shared_ptr<Date> sp4;
    if (sp1) // 等价于sp1.operator bool(),管理资源返回true
        cout << "sp1 is not nullptr" << endl;
    if (!sp4) // 未管理资源返回false
        cout << "sp4 is nullptr" << endl;
    return 0;
}

explicit构造:禁止普通指针隐式转换为智能指针(文末有介绍),避免意外错误:

int main() {
    // 错误:explicit构造禁止隐式转换
    // shared_ptr<Date> sp5 = new Date(2024, 9, 11);
    // unique_ptr<Date> sp6 = new Date(2024, 9, 11);
    return 0;
}

四、智能指针的核心原理

4.1auto_ptr原理(管理权转移)

auto_ptr的核心是 “拷贝时转移资源所有权”,导致原对象悬空,这是其被废弃的根本原因

namespace zephyr {
    template<class T>
    class auto_ptr {
    public:
        auto_ptr(T* ptr) : _ptr(ptr) {}
        // 拷贝构造:转移所有权
        auto_ptr(auto_ptr<T>& sp) : _ptr(sp._ptr) {
            sp._ptr = nullptr; // 原对象悬空
        }
        // 赋值运算符:转移所有权
        auto_ptr<T>& operator=(auto_ptr<T>& ap) {
            if (this != &ap) { // 避免自赋值
                if (_ptr)
                    delete _ptr; // 释放当前资源
                _ptr = ap._ptr; // 转移资源
                ap._ptr = nullptr; // 原对象悬空
            }
            return *this;
        }
        // 析构释放资源
        ~auto_ptr() {
            if (_ptr) {
                cout << "delete:" << _ptr << endl;
                delete _ptr;
            }
        }
        // 模拟指针行为
        T& operator*() { return *_ptr; }
        T* operator->() { return _ptr; }
    private:
        T* _ptr;
    };
}

4.2unique_ptr原理(禁止拷贝)

unique_ptr通过 “删除拷贝构造和赋值运算符” 实现独占所有权,仅支持移动:

namespace zephyr {
    template<class T>
    class unique_ptr {
    public:
        explicit unique_ptr(T* ptr) : _ptr(ptr) {}
        // 移动构造:转移资源所有权
        unique_ptr(unique_ptr<T>&& sp) : _ptr(sp._ptr) {
            sp._ptr = nullptr;
        }
        // 移动赋值:转移资源所有权
        unique_ptr<T>& operator=(unique_ptr<T>&& sp) {
            if (_ptr)
                delete _ptr;
            _ptr = sp._ptr;
            sp._ptr = nullptr;
            return *this;
        }
        // 析构释放资源
        ~unique_ptr() {
            if (_ptr) {
                cout << "delete:" << _ptr << endl;
                delete _ptr;
            }
        }
        // 模拟指针行为
        T& operator*() { return *_ptr; }
        T* operator->() { return _ptr; }
        // 禁止拷贝:删除拷贝构造和赋值运算符
        unique_ptr(const unique_ptr<T>& sp) = delete;
        unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;
    private:
        T* _ptr;
    };
}

4.3shared_ptr原理(引用计数)

shared_ptr的核心是 “引用计数”,通过堆上的计数变量跟踪资源持有者数量:

namespace zephyr {
    template<class T>
    class shared_ptr {
    public:
        // 构造:初始化资源和引用计数(计数初始为1)
        explicit shared_ptr(T* ptr = nullptr)
            : _ptr(ptr)
            , _pcount(new int(1))
            , _del([](T* ptr) { delete ptr; }) {} // 默认删除器
        // 带自定义删除器的构造
        template<class D>
        shared_ptr(T* ptr, D del)
            : _ptr(ptr)
            , _pcount(new int(1))
            , _del(del) {}
        // 拷贝构造:共享资源,引用计数+1
        shared_ptr(const shared_ptr<T>& sp)
            : _ptr(sp._ptr)
            , _pcount(sp._pcount)
            , _del(sp._del) {
            ++(*_pcount);
        }
        // 赋值运算符:释放当前资源,共享新资源
        shared_ptr<T>& operator=(const shared_ptr<T>& sp) {
            if (_ptr != sp._ptr) { // 避免自赋值
                release(); // 释放当前资源(计数-1,为0则删除)
                _ptr = sp._ptr;
                _pcount = sp._pcount;
                _del = sp._del;
                ++(*_pcount); // 新资源计数+1
            }
            return *this;
        }
        // 释放资源逻辑
        void release() {
            if (--(*_pcount) == 0) { // 计数为0,释放资源和计数变量
                _del(_ptr); // 调用删除器
                delete _pcount;
                _ptr = nullptr;
                _pcount = nullptr;
            }
        }
        // 析构:调用release释放资源
        ~shared_ptr() {
            release();
        }
        // 模拟指针行为
        T& operator*() { return *_ptr; }
        T* operator->() { return _ptr; }
        // 实用接口
        T* get() const { return _ptr; } // 获取原生指针
        int use_count() const { return *_pcount; } // 获取引用计数
    private:
        T* _ptr; // 管理的资源指针
        int* _pcount; // 引用计数(堆上分配,支持共享)
        function<void(T*)> _del; // 自定义删除器
    };
}

4.4weak_ptr原理(辅助共享)

weak_ptr不管理资源,仅作为shared_ptr的辅助,不增加引用计数,用于解决循环引用问题

namespace zephyr {
    template<class T>
    class weak_ptr {
    public:
        weak_ptr() : _ptr(nullptr) {}
        // 仅支持通过shared_ptr构造/赋值,不增加引用计数
        weak_ptr(const shared_ptr<T>& sp) : _ptr(sp.get()) {}
        weak_ptr<T>& operator=(const shared_ptr<T>& sp) {
            _ptr = sp.get();
            return *this;
        }
    private:
        T* _ptr; // 仅存储资源指针,不参与管理
    };
}

五、shared_ptr的核心问题与解决方案

5.1 循环引用问题

shared_ptr的共享特性可能导致循环引用,使引用计数无法归零,最终造成内存泄漏。

问题代码(循环引用)

struct ListNode {
    int _data;
    shared_ptr<ListNode> _next; // 共享下一个节点
    shared_ptr<ListNode> _prev; // 共享上一个节点
    ~ListNode() {
        cout << "~ListNode()" << endl; // 循环引用时不会执行
    }
};
int main() {
    shared_ptr<ListNode> n1(new ListNode);
    shared_ptr<ListNode> n2(new ListNode);
    cout << n1.use_count() << endl; // 输出1
    cout << n2.use_count() << endl; // 输出1
    n1->_next = n2; // n2的引用计数变为2
    n2->_prev = n1; // n1的引用计数变为2
    cout << n1.use_count() << endl; // 输出2
    cout << n2.use_count() << endl; // 输出2
    // 析构n1和n2时,引用计数均变为1,无法归零,资源泄漏
    return 0;
}

问题分析

循环引用问题总结分析

1. 循环引用的 “本质原因”:引用计数无法归 0

循环引用导致内存泄漏的核心是:当所有外部 shared_ptr(如 main 中的 n1n2)的生命周期结束后,对象之间的内部 shared_ptr 引用仍然互相维持,使得每个对象的引用计数都大于 0,从而无法触发析构和内存释放。

2. 循环引用的 “结构表现”:不一定是严格的 “环形”,但必然存在 “互相引用的环”

所谓 “循环”,是这种 “引用计数无法归 0” 的典型结构表现,但不是唯一形式。只要存在对象之间通过 shared_ptr 互相引用形成的 “环”,就会导致该问题。

解决方案:weak_ptr

ListNode_next_prev改为weak_ptr,不增加引用计数,打破循环:

struct ListNode {
    int _data;
    weak_ptr<ListNode> _next; // 改为weak_ptr
    weak_ptr<ListNode> _prev; // 改为weak_ptr
    ~ListNode() {
        cout << "~ListNode()" << endl; // 正常执行
    }
};
int main() {
    shared_ptr<ListNode> n1(new ListNode);
    shared_ptr<ListNode> n2(new ListNode);
    n1->_next = n2; // weak_ptr不增加n2的引用计数(仍为1)
    n2->_prev = n1; // weak_ptr不增加n1的引用计数(仍为1)
    // 析构n1和n2时,引用计数均变为0,资源正常释放
    return 0;
}

5.2weak_ptr的实用接口

weak_ptr不直接访问资源(无operator*/operator->),需通过lock()获取shared_ptr,确保资源有效:

int main() {
    shared_ptr<string> sp1(new string("111111"));
    shared_ptr<string> sp2(sp1);
    weak_ptr<string> wp = sp1;
    cout << wp.expired() << endl; // 输出0:资源未过期
    cout << wp.use_count() << endl; // 输出2:获取引用计数
    // sp1和sp2转移资源,原资源释放
    sp1 = make_shared<string>("222222");
    sp2 = make_shared<string>("333333");
    cout << wp.expired() << endl; // 输出1:资源已过期
    cout << wp.use_count() << endl; // 输出0
    // 重新绑定资源
    wp = sp1;
    auto sp3 = wp.lock(); // 资源有效时,返回非空shared_ptr
    if (sp3) {
        *sp3 += "###";
        cout << *sp1 << endl; // 输出"222222###"
    }
    return 0;
}

六、shared_ptr的线程安全问题

6.1 问题本质

问题代码(线程安全隐患)

struct AA {
    int _a1 = 0;
    int _a2 = 0;
    ~AA() {
        cout << "~AA()" << endl;
    }
};
int main() {
    zephyr::shared_ptr<AA> p(new AA);
    const size_t n = 100000;
    mutex mtx;
    // 多线程拷贝智能指针,修改资源对象
    auto func = [&]() {
        for (size_t i = 0; i < n; ++i) {
            zephyr::shared_ptr<AA> copy(p); // 拷贝时修改引用计数(线程不安全)
            unique_lock<mutex> lk(mtx); // 资源对象加锁,保证线程安全
            copy->_a1++;
            copy->_a2++;
        }
    };
    thread t1(func);
    thread t2(func);
    t1.join();
    t2.join();
    cout << p->_a1 << endl; // 可能小于200000(计数竞争导致部分操作失效)
    cout << p->_a2 << endl;
    cout << p.use_count() << endl;
    return 0;
}

6.2 解决方案

将引用计数改为原子类型(atomic<int>),保证计数修改的原子性:

namespace zephyr {
    template<class T>
    class shared_ptr {
    private:
        // 其他成员不变,仅修改引用计数类型
        atomic<int>* _pcount; // 原子类型,保证线程安全
    };
}

七、C++11 与 Boost 智能指针的关系

Boost 库是 C++ 标准库的重要参考,智能指针的发展历程如下:

  1. C++98:推出首个智能指针auto_ptr,设计缺陷明显。
  2. Boost 库:提供scoped_ptr(独占)、shared_ptr(共享)、weak_ptr(辅助)、scoped_array(数组独占)等,实用性更强。
  3. C++ TR1:引入shared_ptr,但非标准正式内容。
  4. C++11:正式引入unique_ptr(对应 Boost 的scoped_ptr)、shared_ptrweak_ptr,实现原理参考 Boost 库。

八、内存泄漏的全面解析

8.1 内存泄漏的定义与危害

示例(无害的内存泄漏)

int main() {
    // 分配1G内存未释放,但程序立即结束,进程退出时系统回收资源
    char* ptr = new char[1024 * 1024 * 1024];
    cout << (void*)ptr << endl;
    return 0;
}

8.2 内存泄漏的检测工具

8.3 内存泄漏的避免方法

  1. 遵循良好的编码规范,手动匹配new/delete(理想状态)。
  2. 优先使用智能指针管理动态资源,利用 RAII 思想自动释放。
  3. 自定义 RAII 类管理特殊资源(如文件句柄、网络连接)。
  4. 项目上线前使用检测工具排查泄漏。

总结

智能指针是 C++ 动态内存管理的核心解决方案,基于 RAII 思想实现资源的自动释放。unique_ptr适用于独占资源场景,shared_ptr适用于共享资源场景,weak_ptr用于解决循环引用问题。掌握智能指针的原理、使用场景及核心问题(循环引用、线程安全),能有效避免内存泄漏,提升代码的健壮性和可维护性。

文章相关问题答疑及讲解:

1.shared_ptr和unique_ptr都得构造函数都使⽤explicit 修饰,防止普通指针隐式类型转换成智能指针对象。

场景模拟:未用 explicit 修饰的智能指针构造函数

假设我们自定义一个简化的智能指针(模拟未加 explicit 的情况):

template<class T>
class MySmartPtr {
public:
    // 未用 explicit 修饰的构造函数(危险!)
    MySmartPtr(T* ptr) : _ptr(ptr) {} 
    ~MySmartPtr() {
        delete _ptr; // 析构时释放内存
        cout << "内存已释放" << endl;
    }
    T& operator*() { return *_ptr; }
private:
    T* _ptr;
};

普通指针的隐式转换案例

定义一个接收智能指针作为参数的函数,然后传递普通指针:

// 函数参数为智能指针类型
void UseResource(MySmartPtr<int> sp) {
    cout << "使用资源:" << *sp << endl;
}
int main() {
    int* raw_ptr = new int(100); // 普通指针
    // 隐式转换:raw_ptr 被自动转换为 MySmartPtr<int> 对象
    UseResource(raw_ptr); 
    // 危险!此时 raw_ptr 指向的内存已被 UseResource 中智能指针的析构函数释放
    cout << *raw_ptr << endl; // 访问已释放内存(未定义行为,可能崩溃)
    delete raw_ptr; // 重复释放(必然崩溃)
    return 0;
}

2.自定义删除器的必要性(何时需要传递析构方法)

只有当需要管理非new分配的资源(如文件句柄、动态库指针、通过 malloc 分配的内存等)时,才需要显式传递自定义删除器。例如:

我的代码的模拟实现中,shared_ptr 的构造函数 explicit shared_ptr(T* ptr, X del) 正是为这种场景设计的 —— 允许用户传递自定义的释放逻辑(del),而默认构造函数 explicit shared_ptr(T* ptr = nullptr) 则使用默认删除器 [](T* ptr) {delete ptr;},这与标准库的设计思路完全一致。

标准库中传递自定义删除器的示例:

#include <memory>
#include <cstdio>
using namespace std;
int main() {
    // 管理 FILE* 资源,自定义删除器用 fclose 释放
    FILE* fp = fopen("test.txt", "w");
    shared_ptr<FILE> sp(fp, [](FILE* p) { 
        fclose(p); 
        cout << "文件已关闭" << endl; 
    }); 
    return 0;
}

3.为什么使用unique_ptr时函数指针 /lambda 也需要传入实例?

1)unique_ptr的模板参数:仅指定删除器的 “类型”

unique_ptr的模板参数class Deleter(第二个参数)的作用是声明删除器的类型,它告诉编译器:“这个unique_ptr将使用某种类型的删除器来释放资源”。

例如:

unique_ptr<Date, void(*)(Date*)> up3;

这里的void(*)(Date*)是一个函数指针类型(指向 “接收Date*参数、返回void的函数” 的指针),它仅声明了 “up3的删除器必须是这种类型的函数指针”,但并没有指定具体用哪个函数指针

2) 构造函数的参数:必须提供删除器的 “实例”

unique_ptr需要知道具体调用哪个删除器来释放资源。模板参数只规定了删除器的类型,而具体的删除器实例(即实际执行删除操作的函数指针)必须通过构造函数传入。

例如,DeleteArrayFunc<Date>是一个符合void(*)(Date*)类型的函数指针实例(它指向真正执行delete[]的函数)。如果不传入这个实例:

// 错误示例:只指定类型,未传入具体删除器实例
unique_ptr<Date, void(*)(Date*)> up3(new Date[5]); 

unique_ptr将无法知道 “到底用哪个函数指针来释放new Date[5]分配的资源”,编译时会报错(缺少删除器实例)。

其实不仅是函数指针,仿函数和 lambda 作为删除器时,都应传入实例,仿函数没有传入实例是因为在unique_ptr构造时隐式构造了一个仿函数实例:

// 仿函数删除器:
unique_ptr<Date, DeleteArray<Date>> up2(new Date[5]); 
unique_ptr<Date, DeleteArray<Date>> up2(new Date[5], DeleteArray<Date>()); 
// (省略时其实是隐式构造了一个实例,等价于上面的写法)
// lambda删除器:传入了delArrOBJ这个实例
unique_ptr<Date, decltype(delArrOBJ)> up4(new Date[5], delArrOBJ);
//lambda表达式的特殊之处在于无法用推导出的类型来隐式构造一个实例
//lambda 闭包类型的构造函数是 “受限的”,因为闭包类型没有默认构造函数,且用户无法手动调用其构造函数

到此这篇关于C++ 智能指针原理、使用与实践的文章就介绍到这了,更多相关C++ 智能指针使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文