• C++智能指针


    智能指针是一种C++语言中的智能化内存管理工具,是一种用来管理动态分配的对象的指针。它的作用是自动化管理内存的分配和释放,以避免内存泄漏和悬空指针的问题。
    智能指针是C++11标准中引入的一个新特性,它们是通过模板类来实现的。
    C++中的智能指针主要有三种:unique_ptr、shared_ptr和weak_ptr。

    1. unique_ptr是独占所有权的指针,只能有一个unique_ptr指向同一个对象,当unique_ptr被销毁时,它所指向的对象也会被销毁。
    2. shared_ptr是共享所有权的指针,多个shared_ptr可以指向同一个对象,当最后一个shared_ptr被销毁时,它所指向的对象也会被销毁。
    3. weak_ptr是一种弱引用,它不会增加对象的引用计数,可以用来解决shared_ptr的循环引用问题。

    使用智能指针可以避免手动管理内存和手动释放对象的问题,可以提高代码的可靠性和安全性。但是需要注意的是,使用智能指针也可能会带来一些性能上的损失,因为智能指针需要进行额外的计数和管理工作。

    shared_ptr(共享指针):

    一种共享指针,是多线程安全的智能指针,它可以使多个指针拥有对同一个对象的控制权,即多个指针可以共享同一个对象。
    当最后一个shared_ptr对象被销毁时,它所管理的对象也会被销毁。
    使用shared_ptr要特别注意循环引用的问题,即两个对象相互引用,导致无法被销毁。
    同时,shared_ptr还提供了自定义删除器的功能,可以在释放资源时调用自定义的函数。

    每个 shared_ptr 对象在内部指向两个内存位置:
    1、指向对象的指针。
    2、用于控制引用计数数据的指针。
    每当有一个新的shared_ptr对象指向同一块内存时,引用计数会自增,当某个shared_ptr对象超出作用域或被显式地设置为nullptr时,引用计数会自减,当引用计数降为0时,shared_ptr便会自动释放其持有的内存。

    创建 shared_ptr 时注意事项:

    1. 不要使用一个原始指针初始化多个shared_ptr
    int *num = new int(23);
    std::shared_ptr<int> p1(num);
    std::shared_ptr<int> p2(p1);  // 正确使用方法
    std::shared_ptr<int> p3(num); // 不推荐
    
    • 1
    • 2
    • 3
    • 4
    1. 在使用 shared_ptr 时,应该优先使用 make_shared 函数来创建 shared_ptr,避免手动管理资源导致的错误;
    std::shared_ptr<int> ptr_1 = make_shared<int>();
    std::shared_ptr<int> ptr_2 (ptr_1);
    
    • 1
    • 2
    1. 不要用栈中的指针构造 shared_ptr 对象
    #include
    #include
    int main() {
        int x = 12;
        std::shared_ptr<int> ptr(&x);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用shared_ptr时需要注意以下几点:

    1. 不要将原始指针和shared_ptr混合使用,否则也可能导致内存泄漏或崩溃。
    2. 不要使用一个原始指针初始化多个shared_ptr 即不要在同一个对象上使用多个shared_ptr,否则会导致计数器混乱,内存泄漏或崩溃。
    3. 当从一个shared_ptr对象中获取对象指针时,应该使用get()成员函数,而不是直接访问shared_ptr对象中的指针。
    4. 当使用循环引用时,多个shared_ptr对象相互持有对方的指针,可能会导致内存泄漏问题。为此,可以使用weak_ptr来解决循环引用问题。
    #include 
    class MyClass {
    public:  
        std::shared_ptr<MyClass> another_instance;  
        MyClass() {std::cout << "Creating MyClass instance\n";  }  
        ~MyClass() {std::cout << "Destroying MyClass instance\n";  }
    };
    int main() {  
        std::shared_ptr<MyClass> instance1 =
          std::make_shared<MyClass>();  
        std::shared_ptr<MyClass> instance2 =
          std::make_shared<MyClass>();  
        instance1->another_instance = instance2;
        instance2->another_instance = instance1;  
        return 0;
    }
    // 这段代码创建了两个MyClass对象,并使它们相互引用。这种做法的弊端在于,两个对象的引用计数永远无法降为0,因为它们相互引用。因此,这会导致内存泄漏,直到程序结束。要避免这种情况,应该避免使用shared_ptr相互引用,或者使用weak_ptr来引用。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1. 当将shared_ptr对象传递给函数或返回一个shared_ptr对象时,建议使用传值方式或者使用std::move()函数,而不是传递指针。
    2. 不要将shared_ptr传递给函数,除非函数需要共享所有权。更好的做法是传递一个const引用或原始指针。
    3. 不要将shared_ptr存储在容器中,例如std::vector。
      shared_ptr存储在容器中的主要弊端是可能导致内存泄漏和悬空指针的问题。当共享指针退出作用域时,如果没有正确的释放内存,它所指向的内存块将一直存在于堆内存中,导致内存泄漏。而当容器中的元素被删除或容器本身被销毁时,在容器中的shared_ptr对象也会被自动删除,这可能导致悬空指针。针对这个问题,可以使用std::weak_ptr或std::unique_ptr来避免内存泄漏和悬空指针的问题。

    shared_ptr自定义删除器功能

    使用场景:

    1. 动态分配的内存不是通过 new 关键字分配的,而是通过其他方式分配的,例如 mmap、malloc 等。
    2. 动态分配的内存需要在释放之前执行一些额外的操作,例如释放资源时需要关闭文件句柄、释放锁等。
      示例代码:
    #include 
    #include 
    
    void my_deleter(int* p) {
        std::cout << "Deleting memory at " << p << std::endl;
        delete p;
    }
    
    int main() {
        std::shared_ptr<int> p(new int(10), my_deleter);
        // 使用 reset 函数传递删除器
        p.reset(new int(20), my_deleter);
        return 0;
    }
    /*
    通过构造函数和 reset 函数分别传递了一个自定义删除器 my_deleter,当 shared_ptr 对象释放资源时,my_deleter 函数会被调用,输出一条消息表示正在删除内存。
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    weak_ptr(弱指针):

    一种多线程安全的指针,一般用于需要访问shared_ptr的场景。不能操作资源。
    weak_ptr指针是一种用于解决 shared_ptr 循环引用问题的智能指针,它不能直接使用对象,必须转化为 shared_ptr 对象后进行使用。
    它指向由shared_ptr控制的对象,不会增加引用计数,也不会阻止被指向对象的销毁,但它本身不控制该对象。当对象被销毁后,weak_ptr会自动失效。

    使用weak_ptr时需要注意以下几点:

    1. 避免直接使用 weak_ptr 指针,可以通过lock函数获取对应的 shared_ptr 对象,再进行操作。
    2. 不要通过 weak_ptr 指针访问对象的成员变量或成员函数,因为对象可能已经被销毁,这样会产生未定义行为。
    3. 如果使用了 shared_ptr 智能指针,则需要小心使用 weak_ptr,因为在 shared_ptr释放底层资源之前,使用 weak_ptr指针可能会导致悬空指针。
    4. 在程序中,尽量减少使用 weak_ptr,只有在必要的场景下才使用,可以避免引发过多的线程安全问题。

    unique_ptr(独占指针):

    一种独享所有权的指针;拥有“独占”这一特性,即在指针对象的生命周期内,它是指向唯一的对象的,其他的独占指针或普通指针都不能指向这个对象。
    它不允许其他的智能指针共享其内部的指针,可以通过它的构造函数初始化一个独占智能指针对象,但是不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。
    当unique_ptr指针被销毁时,它所管理的对象也会随之被销毁,从而保证了对象的唯一性。unique_ptr可以通过移动语义来实现指针的所有权的转让,即将一个unique_ptr的所有权转移给另一个unique_ptr对象。
    使用unique_ptr要注意的是不要将同一对象交给不同的unique_ptr对象控制,避免重复delete的问题。
    使用 unique_ptr 可以防止多个指针同时使用同一块内存,从而避免内存泄漏的问题。

    使用unique_ptr时需要注意以下几点:

    1. unique_ptr是独占式智能指针,即一个unique_ptr拥有指向的对象的所有权,不能被多个指针共享,不能进行复制,只能进行移动操作。
    2. 当unique_ptr被销毁时,它会自动释放指向对象的内存,无需手动释放,从而避免了内存泄漏的问题。
    3. unique_ptr支持自定义释放器,可以通过lambda表达式或自定义函数来实现。
    4. 在使用unique_ptr时,应该避免创建一个指向同一对象的多个unique_ptr,这可能导致不可预期的行为或者程序崩溃。
    5. 可以使用std::move将unique_ptr转移所有权,从而实现指针的所有权转移。
  • 相关阅读:
    Spring MVC中@RequestParam注解的功能是什么呢?
    控制台警报:DevTools failed to load SourceMap
    网络基础(一)
    工程制图知识点
    C#WPF数据触发器实例
    GD32F4(10):GD32转RS422在115200下接收乱码分析
    【vue项目部署】Linux+Nginx实现项目部署及跨域
    upload-labs通关(Pass11-Pass15)
    计算机毕业设计ssm校园扶助综合服务平台的设计与实现r941j系统+程序+源码+lw+远程部署
    Oracle - 多区间按权重取值逻辑
  • 原文地址:https://blog.csdn.net/qq_45780653/article/details/130913974