随笔 - 6  文章 - 0 评论 - 0 阅读 - 282
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

为什么需要智能指针

对于普通指针,在程序结束前我们需要将每个指针都进行free,以免造成内存泄漏。但是手动释放指针是麻烦的,并且一旦漏掉就会造成内存泄漏。因此在C++11中引入智能指针避免此种情况的发生。

智能指针包括std::shared_ptr/std::unique_ptr/std::weak_ptr,需要使用头文件<memory>

引用计数

引用计数是为了防止内存泄漏而引入的概念。想法是基于动态分配的对象,每当增加一次对同一对象的引用,那么引用对象的引用次数就会增加1,每删除一次就减1,当计数减为0时则自动释放指向的堆内存。

std::shared_ptr

std::shared_ptr是一种智能指针,能够记录多少个shared_ptr共同指向同一个对象,从而消除显式地调用delete,当计数变为0时将指针自动删除。

同样地,我们不希望显式地调用new从而使得达成对称,因此引入std::make_shared

#include <iostream>
#include <memory>

void foo(std::shared_ptr<int> i) {
    (*i)++;
}

int main() {
    auto p = std::make_shared<int>(10);
    foo(p);
    std::cout << *p << std::endl; // 11
}

get()可以获取原始指针,use_count()可以查看指针引用计数,reset()可以减少一个引用计数。

#include <iostream>
#include <memory>

int main() {
    auto pointer = std::make_shared<int>(10);
    auto pointer2 = pointer;
    auto pointer3 = pointer;
    int* p = pointer.get();
    std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
    std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
    std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;

    pointer2.reset();

    std::cout << "pointer2.reset()" << std::endl;
    std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
    std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
    std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;

    pointer3.reset();
    std::cout << "pointer3.reset()" << std::endl;
    std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
    std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
    std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;
}
/*
pointer.use_count() = 3
pointer2.use_count() = 3
pointer3.use_count() = 3
pointer2.reset()
pointer.use_count() = 2
pointer2.use_count() = 0
pointer3.use_count() = 2
pointer3.reset()
pointer.use_count() = 1
pointer2.use_count() = 0
pointer3.use_count() = 0
*/

std::unique_ptr

std::unique_ptr是一种独占的智能指针,禁止与其他智能指针共享一个对象,从而保证代码的安全。

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> pointer = std::make_unique<int>(10);
    auto pointer2 = pointer;
    std::cout << *pointer2 << std::endl;
}

很明显这样会报错

错误  C2280   “std::unique_ptr<int,std::default_delete<int>>::unique_ptr(const std::unique_ptr<int,std::default_delete<int>> &)”: 尝试引用已删除的函数  shared_pointer  C:\\Users\\hasee\\Desktop\\code\\test\\shared_pointer\\test_1.cpp  6

虽然std::unique_ptr是独占,不可转移,我们可以使用C++11中的std::move()转移给其他std::unique_ptr。转移并不是复制,例如p1转移给空指针p2后,p2变成p1p1变成空。

#include <iostream>
#include <memory>

struct Foo {
    Foo() { std::cout << "Foo::Foo" << std::endl; }
    ~Foo() { std::cout << "Foo::~Foo" << std::endl; }
    void foo() { std::cout << "Foo::foo" << std::endl; }
};

void f(const Foo &) {
    std::cout << "f(const Foo&)" << std::endl;
}

int main() {
    std::unique_ptr<Foo> p1(std::make_unique<Foo>());
    if (p1) p1->foo(); // 不空
    {
        std::unique_ptr<Foo> p2(std::move(p1));
        f(*p2); 
        if (p2) {
            p2->foo(); // 不空
        }
        if (p1) {
            p1->foo();
        }
        p1 = std::move(p2); // 空
        if (p2) {
            p2->foo(); // 空
        }
        std::cout << "p2 gg" << std::endl;
    }
    if (p1) {
        p1->foo();
    }
}

std::weak_ptr

std::shared_ptr虽然相比普通指针优化了内存泄漏的情况,但是仍然存在内存泄漏的可能。

仔细观察可以发现std::shared_ptr通过引用计数销毁指针是做了一个根据依赖关系类似拓扑排序的删点过程,那么也就是说如果依赖关系存在环,那么std::shared_ptr同样无法释放资源,造成内存泄漏。

因此引入std::weak_ptr,弱引用不会使引用计数增加,因此在析构时可以销毁。

std::weak_ptr没有*->运算符,因此不能对资源进行操作,而可以用于检查std::shared_ptr是否存在。

std::weak_ptr通常不单独使用,和std::shared_ptr搭配使用,是一种辅助工具。

内容主要来自https://changkun.de/modern-cpp/zh-cn/05-pointers/

posted on   lgats324  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示