Connection是一个管理连接类,在释放Connection之前,我们需要调用close函数来关闭连接。观察如下代码:

#include <iostream>
#include <memory>
#include <string>
using namespace std;
class Connection{
public:
    explicit Connection(string name):_name(name){
    string get_name() const {
        return _name;
private:
    string _name;
void close(Connection* connection){
    cout << string("关闭")+connection->get_name()+"管理的连接中..." << endl;
    //关闭连接的代码
    // .....
    cout << "关闭完成。" << endl;
int main(){
    // 新建管理连接Connection的智能指针
    shared_ptr<Connection> sp(new Connection);
    unique_ptr<Connection> up(new Connection);

执行上述代码,发现并没有办法调用close函数,因为控制权完全在shared_ptr/unique_ptr中。你可能会说,在退出作用域之前我调用close(sp.get())先关闭连接,这样不就可以了嘛?实际上,这种做法对于shared_ptr并不安全,手动close之后,不能确保sp管理的Connection只有一份拷贝(即sp中的计数器多于1)。因此,需要使用自定义的删除器。

为了节省篇幅,后面代码中不在贴出公共的代码。

一、使用函数

删除函数定义类似于:

void Deleter(T *val){
    // 其他代码
    // 释放val的内存
    delete val;
    // 或者(如果val是数组)
    delete[] val;

T是shared_ptr/unique_ptr管理的类型,val是指针,可以指向一个实例,也可以是数组的首地址(参考:使用shared_ptr/unique_ptr管理数组),取决于shared_ptr/unique_ptr管理的具体内容。
原理是:当删除器的指针Deleter传给shared_ptr/unique_ptr时,shared_ptr/unique_ptr不会使用默认的delete val来释放其管理的资源,而是使用Deleter(val)来释放资源,这样就调用了Deleter来释放管理的资源。后面的各种方式的原理也是如此。

// 函数式删除器
void Deleter(Connection *connection){
    close(connection);
    delete connection;
int main(){
    // 新建管理连接Connection的智能指针
    shared_ptr<Connection> sp(new Connection("shared_ptr"), Deleter);
    unique_ptr<Connection, decltype(Deleter)*> up(new Connection("unique_ptr"), Deleter);

shared_ptr在使用的时候,只需要把函数式删除器的指针传给构造函数就行;而unique_ptr还用增加一个模板参数decltype(Deleter)*,这是shared_ptr和shared_ptr的不同点之一(注意:unique_ptr的第二个模板参数是指针)。

二、使用可调用类

可调用类是指重载了调用运算符的类。可调用的对象的好处是它也是一个类,可以用来保存一些状态。

class DeleterClass{
public:
    DeleterClass():_count(0){}
     * 重载调用运算符。
     * 这里要定义成模板函数,才可以在unique_ptr中使用。
    template <typename T>
    void operator ()(T *connection){
        close(connection);
        delete connection;
private:
    int _count;
int main(){
    // 新建管理连接Connection的智能指针
    shared_ptr<Connection> sp(new Connection("shared_ptr"), DeleterClass());
    DeleterClass dc;
    unique_ptr<Connection, DeleterClass> up(new Connection("unique_ptr"), dc);
    unique_ptr<Connection, DeleterClass> up1(new Connection("unique_ptr2"), up.get_deleter());

unique_ptr的第二个模板参数是类类型,构造函数的第二个参数是可调用类的实例。

三、使用lambda表达式

目前最简单的自定义删除器(好爽啊,还有如此简洁的代码)。

int main(){
    auto DeleterLambda=[](Connection *connection){
        close(connection);
        delete connection;
    // 新建管理连接Connection的智能指针
    shared_ptr<Connection> sp(new Connection("shared_ptr"), DeleterLambda);
    unique_ptr<Connection, decltype(DeleterLambda)> up(new Connection("unique_ptr"), DeleterLambda);

四、使用std::function

使用这种形式讲真没有第一种简单,优势在于std::function的特点(比如参数绑定等等)。

void Deleter(Connection *connection){
    close(connection);
    delete connection;
int main(){
    std::function<void (Connection*)> deleter(Deleter);
    // 新建管理连接Connection的智能指针
    shared_ptr<Connection> sp(new Connection("shared_ptr"), deleter);
    unique_ptr<Connection, decltype(deleter)> up(new Connection("unique_ptr"), deleter);
                    默认情况下,智能指针使用delete释放其管理的资源,有时候,可能要修改默认使用delete释放资源的行为。本文将列出我所知道的所有自定义删除器的方法。目录零、引例一、使用函数二、使用可调用类三、使用lambda表达式四、使用std::function零、引例Connection是一个管理连接类,在释放Connection之前,我们需要调用close函数来关闭连接。观察如下代码:#incl...
				
问题引出:智能指针默认删除做的事情太简单啦,仅仅调用delete释放资源可能还不够 unique_ptr类: //第一个参数智能指针底层资源的类型,第二个参数是删除,就是下面struct default_delete //在删除资源时会调用删除的operator()运算符重载,默认调用的是delete ptr. template<class _Ty,class _Dx> // = default_delete<_Ty> class unique_ptr{ //...
1、智能指针删除类 因为用自己实现的内存池在释放内存时需要buffer的大小才可以,用函数和labda表达式不知道如何实现,所以用删除类实现自定义智能指针删除。 /* 智能指针删除类 */ class CustomerDeleter public: CustomerDeleter(shared_ptr<RingMempool> MemPool, siz...
智能指针是用来实现指针指向的对象的共享的。其实现的基本思想: 每次创建类的新对象时,初始化指针并将引用计数置为1; 当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数; 对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数减至0,则删除对象),并增加右操作数所指对象的引用计数; 调用析构函数时,减少引用计数(如果引用计数减至0,则删除基础对象); 重载“->”以及“*”操作符,使得智能指针有类似于普通指针的操作。
std::unique_ptr 占用空间与裸指针相同,大多数操作(包括解引用)的方法也相同。它表现的是 独占性拥有(exclusive ownership) 的语义,这意味着它不能被拷贝,是 move-only 类型;当其自身被销毁时,总会销毁其拥有的资源。而对象的销毁在作用域结束(即右大括号出现)时自动通过调用析构函数进行,此时 std::unique_ptr 默认对内部的裸指针进行 delete,因此你无需像裸指针一样操心什么时候要手动销毁。 (注:笔者尽己所能对MSVC的 std::unique_p.
1.1 智能指针 为了避免手动在堆中分配出的内存没有释放造成内存泄露的问题,C++11提供了智能指针智能指针将指针封装成一个栈对象。栈对象在生命周期结束自动销毁时会调用析构函数。智能指针基本上也是在析构函数中做文章,实现的堆上内存管理。 C++11推荐使用的智能指针unique_ptrshared_ptr和weak_ptr三种。 1.2 内容简介 本文为std::unique_ptr的实现篇,介绍实现一个UniquePtr类,实现与std::unique_ptr类似的功能。代码实现参考了st
C++智能指针shared_ptr讲解与使⽤ ⼿动管理的弊端 在简单的程序中,我们不⼤可能忘记释放 new 出来的指针,但是随着程序规模的增⼤,我们忘了 delete 的概率也随之增⼤。在 C++ 中 new 出来的指针,赋值意味着引⽤的传递,当赋值运算符同时展现出"值拷贝"和"引⽤传递"两种截然不同的语义时,就很容易导致"内 存泄漏"。 ⼿动管理内存带来的更严重的问题是,内存究竟要由谁来分配和释放呢?指针的赋值将同⼀对象的引⽤散播到程序各处,但是该对象的释放 却只能发⽣⼀次。当在代码中⽤完了⼀个资源指针,该不该释放 delete 掉它?这个资源极有可能同时被多个对象拥有着,⽽这些对象中的 任何⼀个都有可能在之后使⽤该资源,其余指向这个对象的指针就变成了"野指针";那如果不 delete 呢?也许你就是这个资源指针的唯 ⼀使⽤者,如果你⽤完不 delete,内存就泄漏了。 资源的拥有者是系统,当我们需要时便向系统申请资源,当我们不需要时就让系统⾃⼰收回去(Garbage Collection)。当我们⾃⼰处理的时 候,就容易出现各种各样的问题。 C++中的智能指针C++11起,C++标准提供两⼤类型的智能指针: 1. Class shared_ptr实现共享式拥有(shared ownership)概念。多个智能指针可以指向相同对象,该对象和其相关资源会在"最后⼀个引 ⽤(reference)被销毁"时候释放。为了在结构复杂的情境中执⾏上述⼯作,标准库提供了weak_ptr、bad_weak_ptr和 enable_shared_from_this等辅助类。 2. Class unique_ptr实现独占式拥有(exclusive ownership)或严格拥有(strict ownership)概念,保证同⼀时间内只有⼀个智能指针 可以指向该对象。它对于避免资源泄露(resourece leak)——例如"以new创建对象后因为发⽣异常⽽忘记调⽤delete"——特别有⽤。 注:C++98中的Class auto_ptrC++11中已不再建议使⽤。 share_ptr 智能指针是(⼏乎总是)模板类,shared_ptr 同样是模板类,所以在创建 shared_ptr 时需要指定其指向的类型。shared_ptr 负责在不使 ⽤实例时释放由它管理的对象,同时它可以⾃由的共享它指向的对象。 shared_ptr 使⽤经典的 "引⽤计数" 的⽅法来管理对象资源。引⽤计数指的是,所有管理同⼀个裸指针( raw pointer )的 shared_ptr,都共享⼀个引⽤计数,每当⼀个 shared_ptr 被赋值(或拷贝构造)给其它 shared_ptr 时,这个共享的引⽤计数就加 1,当⼀个 shared_ptr 析构或者被⽤于管理其它裸指针时,这个引⽤计数就减1,如果此时发现引⽤计数为0,那么说明它是管理这个 指针的最后⼀个 shared_ptr 了,于是我们释放指针指向的资源。 在底层实现中,这个引⽤计数保存在某个内部类型⾥(这个类型中还包含了 deleter,它控制了指针的释放策略,默认情况下就是普通的 delete操作),⽽这个内部类型对象在 shared_ptr 第⼀次构造时以指针的形式保存在 shared_ptr 中(所以⼀个智能指针的析构会影响到 其他指向同⼀位置的智能指针)。shared_ptr 重载了赋值运算符,在赋值和拷贝构造另⼀个 shared_ptr 时,这个指针被另⼀个 shared_ptr 共享。在引⽤计数归零时,这个内部类型指针与 shared_ptr 管理的资源⼀起被释放。此外,为了保证线程安全性,引⽤计数 的加1,减1操作都是 原⼦操作,它保证 shared_ptr 由多个线程共享时不会爆掉。 对于 shared_ptr 在拷贝和赋值时的⾏为,《C++Primer第五版》中有详细的描述: 每个 shared_ptr 都有⼀个关联的计数值,通常称为引⽤计数。⽆论何时我们拷贝⼀个 shared_ptr,计数都会递增。 例如,当⽤⼀个 shared_ptr 初始化另⼀个 shred_ptr,或将它当做参数传递给⼀个函数以及作为函数的返回值时,它所关联的计数 就会递增。当我们给 shared_ptr 赋予⼀个新值或是 shared_ptr 被销毁(例如⼀个局部的 shared_ptr 离开其作⽤域)时,计数 就会递减。⼀旦⼀个 shared_ptr 的计数变为0,它就会⾃动释放⾃⼰所管理的对象。 下⾯看⼀个常见⽤法,包括: 1. 创建 shared_ptr 实例 2. 访问所指对象 3. 拷贝和赋值操作 4. 检查引⽤计数。 #include <iostream> #include
C++中的智能指针首先出现在“准”标准库boost中。 随着使用的人越来越多,为了让开发人员更方便、更安全的使用动态内存,C++11也引入了智能指针来管理动态对象。 在新标准中,主要提供了shared_ptrunique_ptr、weak_ptr三种不同类型的智能指针。 接下来的几篇文章,我们就来总结一下这些智能指针的使用。 今天,我们先来看看shared_ptr智能指针shared_ptr 智能指针 shared_ptr是一个引用计数智能指针,用于共享对象的所有权也就是说它允许多个指针指向同一个对象。这一点与原始指针一致。 先来一段简单的代码,看看shared_ptr的简单使用:
我们前面介绍了unique_ptr智能指针,它对它所指向的对象资源具有专属所有权。这个就直接导致unique_ptr是无法进行复制操作的。有没有一种智能指针对象资源不具有专属所有权,也就是它可以进行复制操作。当然有的。那就是shared_ptr智能指针shared_ptr也是对裸指针进行包装的类。 shared_ptr智能指针对它所指涉的对象资源具有共享所有权,也就是说指涉到该对象资源的所有的shared_ptr共同协作,确保在不再需要该对象的时刻将其进行析构。当最后一个指涉到该资源的shared_ptr不在指涉到对象资源时,该shared_ptr会对该对象资源进行析构。怎么判断是否是最后一
当我们使用QMultiMap的remove函数来删除中的某一个元素时,若是容中存放的是基本数据类型,则程序编译的时候没有任何问题,但是当容中存放的是自定义的数据,这时若是C++基础比较扎实的,应该不会出现编译报错的问题,但是C++j基础不扎实的,就不会在自定义数据类型中重写赋值运算符函数,故而导致使用remove删除中的元素时,会出现编译错误。 其实上述大致提到是因为自定义数据类型需要重写赋值运算符。故而在自定义数据类型中重写赋值运算符便可编译正常。究其原因是因为remove函数本身
shared_ptrunique_ptr都是C++11中引入的指针类型,它们在智能指针管理下的动态内存分配中起着重要作用。 shared_ptr是一种计数型智能指针,可以动态地掌控一块内存,在创建新的shared_ptr对象时计数增加,同时将该内存空间的控制权转移给该新对象。当引用计数为0时,该内存在控制权转移到的最后一个对象销毁后会自动释放。 相对于shared_ptrunique_ptr是一种独占型智能指针,在内存控制权转移时不适用计数,而是将控制权完整地转移到新对象中,从而实现内存的动态管理。unique_ptr在不需要再共享相关资源时不仅更快也更安全,可以防止使用不同指针同时访问某一块内存所引发的问题。 weak_ptr是一种弱引用型智能指针,它可以引用shared_ptr所管理的内存空间,但不会改变内存的引用计数,这意味着,当其他所有shared_ptr指针被销毁时,被weak_ptr暗示的内存却不会自动释放,避免了悬空指针或内存泄漏的问题。通过lock()函数可以将weak_ptr转换为对应的shared_ptr,如果锁定成功,就可以用shared_ptr安全地访问该内存空间了。 因此,shared_ptrunique_ptr各有优缺点,需要视情况选择使用。而weak_ptr则是shared_ptr的补充,可以避免悬空指针、内存泄漏等问题。