智能指针(第四章)(条款18,19,20,21)

条款18:使用 std::unique_ptr 管理具备专属所有权的资源

要点速记

*std::unique_ptr 是小巧,高速、具备只移型别的智能指针,对托管资源实施专属所有权语义

*默认的,资源析构采用 delete 运算符来实现,但是可以指定自定义删除器,有状态的删除器或者函数指针实现的删除器会增加 std::unique_ptr 型别的对象尺寸,因为删除器是其一部分

*无状态的函数对象,无捕获的 lambda 表达式不会增加智能指针的尺寸,应该优先使用

*将 std::unique_ptr 转换为 std::shared_ptr 很容易实现

*常用于 工厂函数 和 pimpl实现

    class Investment {
    public:
        Investment() = default;

        ~Investment() = default;
    };

    class Stock : public Investment {
        using Investment::Investment;
    };

    class Bond : public Investment {
        using Investment::Investment;
    };

    class RealEstate : public Investment {
        using Investment::Investment;
    };

    // 默认删除器
    unique_ptr<Investment> makeInvestment(int i) {
        unique_ptr<Investment> pInv;
        if (i < 0) {
            pInv.reset(new Stock());
        } else if (i == 0) {
            pInv.reset(new Bond());
        } else if (i > 0) {
            pInv.reset(new RealEstate());
        }
        return pInv;
    }

    // 自定义删除器,注意,函数的返回类型采用auto,即自动推导
    auto delInvmt = [](Investment *pInvmt) {
        std::cout << "logging inof..." << std::endl;
        delete pInvmt;
    };

    template<typename... TS>
    auto makeInvestment(TS... params) {
        // 自定义删除器就不能默认构造了
        unique_ptr<Investment, decltype(delInvmt)> pInv(nullptr, delInvmt);
        if (sizeof...(params) == 1) {
            pInv.reset(new Stock());
        } else if (sizeof...(params) == 2) {
            pInv.reset(new Bond());
        } else if (sizeof...(params) == 3) {
            pInv.reset(new RealEstate());
        }
        return pInv;
    }

    // 测试
    void UniquePtrTest1() {
        auto up1 = makeInvestment(-1);
        auto up2 = makeInvestment(0);
        auto up3 = makeInvestment(1);

        {
            auto up4 = makeInvestment("hello");
            auto up5 = makeInvestment("hello", "world");
            auto up6 = makeInvestment("hello", "world", "hello motherland");
        }

        return;
    }

条款19:使用 std::shared_ptr 管理具备共享所有权的资源

要点速记

*std::shared_ptr 提供方便的手段,实现任意资源在共享所有权语义下进行生命周期管理的垃圾回收

*与std::unique_ptr 相比,std::shared_ptr 的尺寸通常是裸指针的两倍,它还会带来控制块的开销,并要求原子化的引用计数操作

*默认的资源析构通过 delete 运算符进行, 但同时也支持定制删除器, 删除器的类型对 std::shared_ptr 的型别没有影响,不是其对象的直接部分

*避免使用裸指针型别的变量来创建 std::shared_ptr 指针

    class Widget {
    public:
        Widget() = default;

        Widget(int p) : size(p) {}

    private:
        int size;
    };


    void sharedPtrTest1() {
        // 自定义删除器
        auto loggingDel = [](Widget *pw) {
            std::cout << "logging info..." << std::endl;
            delete pw;
        };

        // unique_ptr
        unique_ptr<Widget, decltype(loggingDel)> upw1(new Widget(), loggingDel); // loggingDel是unique_ptr一部分
        auto upw2(std::make_unique<Widget>()); // 默认删除器
        auto upw3(std::make_unique<Widget>(1)); // 默认删除器

        // shared_ptr
        shared_ptr<Widget> spw(new Widget, loggingDel); // loggingDel不是shared_ptr的一部分
        auto sp2(std::make_shared<Widget>()); // 默认删除器
        auto sp3(std::make_shared<Widget>(1)); // 默认删除器

        // 不同的自定义删除器对shared_ptr型别无影响
        auto customDeleter1 = [](Widget *pw) {
            std::cout << "customDeleter1 logging info..." << std::endl;
            delete pw;
        };

        auto customDeleter2 = [](Widget *pw) {
            std::cout << "customDeleter2 logging info..." << std::endl;
            delete pw;
        };

        shared_ptr<Widget> spw11(new Widget, customDeleter1);
        shared_ptr<Widget> spw22(new Widget, customDeleter2);
        std::vector<shared_ptr<Widget>> vpw{spw11, spw22}; // 瞧,不同删除器没有影响,还是同一种类型

        return;
    }

条款20:对于类似 std::shared_ptr 但有可能空悬的指针使用 std::weak_ptr

要点速记

*使用 std::weak_ptr 来代替可能空悬的 std::shared_ptr

*std::weak_ptr 可能的用武之地包括缓存, 观察者列表, 以及避免 shared_ptr 指针环路(在树这种严格继承谱系式的数据结构中,也可直接用裸指针来实现环路避免)

    void weakPtrTest1() {
        auto spw = std::make_shared<Widget>();
        weak_ptr<Widget> wpw(spw);
//        spw = nullptr;

        // weak_ptr测试指针是否空悬,有效性测试
        if (wpw.expired()) {
            std::cout << "the object is deleted..." << std::endl;
        }

        // 两种常用操作形式
        // 1)lock, 若wpw失效,则spw1为空
        auto spw1 = wpw.lock();
        // 2)weak_ptr作为实参来构造shared_ptr,如果wpw失效,则抛出异常
        shared_ptr<Widget> spw2(wpw);

        return;
    }

条款21:优先选用 std::make_unique 和 std::make_shared, 而非直接使用 new

要点速记

*相比于直接使用 new 表达式, make 系列函数消除了重复代码,改进了异常安全性,并且对于 make_shared 和 allcoated_shared 而言,生成的目标代码会尺寸更小,速度更快

*不适于使用 make 系列函数的场景包括需要定制删除器, 以及期望直接传递 {} 初始化物

*对于 shared_ptr, 不建议使用 make 系列函数的额外场景包括:1)自定义内存管理的类; 2)内存紧张的系统、非常大的对象、以及存在比指涉到相同对象的 shared_ptr 生存期更久的 weak_ptr

    int computePriority() {}

    void processWidget(shared_ptr<Widget> spw, int priority) {}

    void makePtrTest1() {
        // 比较
        processWidget(shared_ptr<Widget>(new Widget()), computePriority()); // 潜在的资源泄露,引发两次内存分配
        processWidget(std::make_shared<Widget>(), computePriority()); // 不会发生潜在资源泄露的风险,一次内存分配

        // 需要自定义删除器的场景不适合使用make系函数
        auto widgetDeleter = [](Widget *pw) { delete pw; };
        shared_ptr<Widget> spw(new Widget, widgetDeleter);

        // 需要传递{}之物做初始化
        auto upv = std::make_unique<vector<int>>(5, 20); // {20,20,20,20,20,20}
        auto spv = std::make_shared<vector<int>>(5, 20); // {20,20,20,20,20,20}
        auto iniList = {5, 20};
        auto spv1 = std::make_shared<vector<int>>(iniList); // {5, 20}

        // make_shared创建的内存是依次性分配的,存在有 weak_ptr 指涉相同对象的情况下,shared_ptr析构完成本来应该释放的对象内存没有释放
        // 因为这个时候可能weak_ptr的生成期仍然存在,导致控制块需要使用
        // todo


        // processWidget(shared_ptr<Widget>(new Widget(), widgetDeleter), computePriority());的改进
        processWidget(shared_ptr<Widget>(new Widget(), widgetDeleter), computePriority()); // 存在资源泄露风险
        // 改进一,不存在资源泄露问题
        std::shared_ptr<Widget> spw1(new Widget, widgetDeleter);
        processWidget(spw1, computePriority()); // shared_ptr的拷贝涉及引用计数变化,涉及到原子操作,需要考虑性能问题
        // 改进二,改进性能
        processWidget(std::move(spw1), computePriority()); // 提示性能

        return;
    }

猜你喜欢

转载自blog.csdn.net/u010323563/article/details/112971281
今日推荐