C++智能指针(半自动的,需要手动创建) --------动态内存管理工具

所有的智能指针都是基于模板泛型

一.auto_ptr

        C++17里面废除了,C++11标记废弃,弃用的原因是存在重大的缺陷和安全问题

1.用法

#include <iostream>
#include <vector>
using namespace std;
class Person
{
public:
    Person()
    {
        cout << "Person 构造函数" << endl;
    }
    ~Person()
    {
        cout << "Person 析构函数" << endl;
    }
};
void test()
{    
    auto_ptr<Person> ptr(new Person);
    // auto_ptr 不适合用于管理动态内存的数组对象。
    //auto_ptr<Person[]> ptr(new Person[10]);
    // 获得资源指针
    cout << ptr.get() << endl;
    
    // 释放资源所有权,即:ptr 不再持有动态资源
    Person* p = ptr.release();
    // 释放资源,并指向新的资源
    ptr.reset(new Person);
    // 只释放资源
    ptr.reset();
}
int main()
{    
    test();
    return 0;
}

        2.注意:

                1.auto_ptr底层使用的是delete,所以这是不合适管理动态的内存数组对象;

                2.ptr.release释放资源的所有权,那么原来的对象需要我们自己手动释放;

        3.auto_ptr缺陷

                1.auto_ptr不能共享所有权,多个指针不能指向同一个对象

                2.auto_ptr内部没有拷贝构造函数的,就不能放在容器里面,auto_ptr在拷贝或者赋值 时进行的是资源所有权转移,而并不是真正的拷贝和赋值,会发生所有权转移

// 1. 不能共享所有权
void function(auto_ptr<Person> person) {}
void test01()
{
    auto_ptr<Person> ptr1(new Person);
    // 将 ptr1 传递到 ptr2 的有参构造中进行初始化
    auto_ptr<Person> ptr2 = ptr1;
    cout << "ptr1:" << ptr1.get() << " ptr2:" << ptr2.get() << endl;
    // 赋值发生所有权转移
    auto_ptr<Person> ptr3;
    ptr3 = ptr2;
    cout << "ptr2:" << ptr2.get() << " ptr3:" << ptr3.get() << endl;
    function(ptr3);
    cout << "ptr3:" << ptr3.get() << endl;
}

不能放在容器

    auto_ptr<Person> ptr1(new Person);
    auto_ptr<Person> ptr2(new Person);
    // 容器要求元素要能够被拷贝
    vector<auto_ptr<Person>> vec;
    // 左值
    // vec.push_back(ptr1);
    // vec.push_back(ptr2);
    // 添加右值(临时对象)
    vec.push_back(auto_ptr<Person>(new Person));
    auto v = vec[0];
    cout << vec[0].get() << endl;

会报错,所以不能作为容器的元素

添加右值就不会报错,但是也是会有问题

2.unique:独占所有权管理指针,不能多个指针指向不同的对象,类似于auto_ptr;

1.用法

#include <iostream>
using namespace std;
class Person
{
public:
    Person()
    {
        cout << "构造函数" << endl;
    }
    void Demo()
    {
        cout << "Person Demo" << endl;
    }
    ~Person()
    {
        cout << "析构函数" << endl;
    }
};
// 1. unique_ptr 创建方式
void test01()
{
    // 1.1 管理单一动态对象
    unique_ptr<Person> up1(new Person);
    up1->Demo();
    // 1.2 管理动态对象数组
    unique_ptr<Person[]> up2(new Person[2]);
    up2[0].Demo();
    up2[1].Demo();
}
// 2. unique_ptr 操作函数
void test02()
{
    unique_ptr<Person> up(new Person);
    // 2.1 get 成员函数返回 unique_ptr 管理的动态对象指针
    Person* person1 = up.get();
    // 2.2 release 成员函数使 unique_ptr 不再持有动态对象指针(并不销毁管理的对象),并返回其管理的动态指针。
    Person* person2 = up.release();
    delete person2;
    // 2.3 reset 成员函数有两个重载的版本,具体功能如下:
    up.reset();  // 释放并销毁 unique_ptr 所管理的动态对象指针。
    up.reset(new Person);  // 释放并销毁原来的管理的动态对象,并重新持有新创建的动态对象
    // 2.4 swap 成员函数交换两个 unique_ptr 对象管理的动态对象
    unique_ptr<Person> sp(new Person);
    up.swap(sp);
}
int main()
{
    // test01();
    test02();
    return 0;
}

2.特性

  1. 同时只能有一个 unique_ptr 对象来持有动态对象资源
  2. unique_ptr 对象不支持默认拷贝、默认赋值语义
  3. unique_ptr 对象支持移动拷贝、移动赋值语义
  4. unique_ptr 对象不能以值的方式用做函数参数,也不能存储到STL 的容器
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class Person
{
public:
    Person()
    {
        cout << "构造函数" << endl;
    }
    ~Person()
    {
        cout << "析构函数" << endl;
    }
};
void test()
{
    unique_ptr<Person> up1(new Person);
    unique_ptr<Person> up2(new Person);
    // 1. 禁止拷贝、赋值
    // unique_ptr(const unique_ptr&)            = delete;
    // unique_ptr& operator=(const unique_ptr&) = delete;
    // 2. 允许移动拷贝、赋值
    unique_ptr<Person> up3(move(up1));  // 移动拷贝
    up2 = move(up3);  // 移动赋值
    // 由此, unique_ptr 不允许作为容器元素
    // vector<unique_ptr<Person>> vec;
    // vec.push_back(up1);
}
int main()
{
    test();
    return 0;
}

拷贝赋值的时候编译器会报错,明确禁止的

3.unique_ptr 自定义删除器

unique_ptr 可用于管理 new 出来的动态对象,也可以管理其他非new需要手动关闭的资源。例如:文件对象。

由于 unique_ptr 默认使用 delete、delete[] 来释放被管理的资源。所以,当管理的对象不能通过 delete、delete[] 来释放时,就需要自定义删除器

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <functional>
using namespace std;
class Person
{
public:
    Person()
    {
        cout << "构造函数" << endl;
    }
    ~Person()
    {
        cout << "析构函数" << endl;
    }
};
struct Deleter
{
    void operator()(FILE* fp)
    {
        cout << "文件被自动关闭" << endl;
        if (fp != nullptr)
        {
            fclose(fp);
            fp = nullptr;
        }
    }
};
void my_deleter(FILE* fp)
{
    cout << "文件被自动关闭" << endl;
    if (fp != nullptr)
    {
        fclose(fp);
        fp = nullptr;
    }
}
void test()
{
    // 1. 函数对象作为删除器
    // unique_ptr<FILE, Deleter> up(fopen("./demo.txt", "w"), Deleter());
    // unique_ptr<FILE, function<void(FILE*)>> up(fopen("./demo.txt", "w"), Deleter());
    
    // 2. 普通函数作为删除器
    // unique_ptr<FILE, decltype(&my_deleter)> up(fopen("./demo.txt", "w"), my_deleter);
    // unique_ptr<FILE, void(*)(FILE *)> up(fopen("./demo.txt", "w"), my_deleter);
    // unique_ptr<FILE, function<void(FILE*)>> up(fopen("./demo.txt", "w"), my_deleter);
    
    // 3. 匿名函数作为删除器
    unique_ptr<FILE, function<void(FILE *)>> up(fopen("./demo.txt", "w"), [](FILE *fp) {
            cout << "文件被自动关闭" << endl;
            if (fp != nullptr)
            {
                fclose(fp);
                fp = nullptr;
            }
        });
    if (!up)
    {
        cout << "文件打开失败" << endl;
        return;
    }
    fputs("hello world\n", up.get());
}
int main()
{
    test();
    return 0;
}

3.shared_ptr

C++ 的 shared_ptr 是 C++11 标准引入的智能指针之一,用于管理动态分配的对象的所有权。它允许多个 shared_ptr 实例共享对同一对象的所有权,而不会出现内存泄漏或者悬空指针的情况。shared_ptr 使用引用计数技术来跟踪有多少个 shared_ptr 实例指向同一个对象,并在最后一个实例销毁时自动释放对象;

1.用法

#include <iostream>
using namespace std;
class Person
{
public:
    Person()
    {
        cout << "无参构造函数" << endl;
    }
    Person(int, int)
    {
        cout << "有参构造函数" << endl;
    }
    ~Person()
    {
        cout << "析构函数" << endl;
    }
};
// 1. 创建 shared_ptr 对象
void test01()
{
    // 1.1 使用 shared_ptr 的构造函数创建对象
    shared_ptr<Person> sp1(new Person(10, 20));
    // 1.2 使用 shared_ptr 管理动态对象数组
    shared_ptr<Person[]> sp2(new Person[5]);
}
// 2. shared_ptr 操作函数
void test02()
{
    shared_ptr<Person> sp1(new Person(10, 20));
    shared_ptr<Person> sp2(new Person(100, 200));
    // 2.1 get 成员函数可以获得 shared_prt 管理的动态对象指针
    Person* person = sp1.get();
    // 2.2 swap 成员函数可以交换两个 shared_ptr 管理的动态对象指针
    sp1.swap(sp2);
    // 2.3 reset 成员函数存在两个重载版本的函数,其作用分别如下:
    sp1.reset();  // 释放其管理动态指针,此时 sp1 对象管理的动态指针指向为 nullptr
    sp1.reset(new Person(1, 2));  // 释放原来的动态对象,并指向新的动态对象
}
int main()
{
    test01();
    test02();
    return 0;
}

2.特性

shared_ptr 叫做共享智能指针,多个 shared_ptr 对象能够同时持有并管理同一个动态对象。当 shared_ptr 发生对象拷贝、赋值时都会导致多个 shared_ptr 对象持有同一个动态对象

#include <iostream>
#include <vector>
using namespace std;
class Person
{
public:
    Person()
    {
        cout << "无参构造函数" << endl;
    }
    ~Person()
    {
        cout << "析构函数" << endl;
    }
};
void test()
{
    shared_ptr<Person> sp1(new Person);
    // 1. 允许对象拷贝、对象赋值
    shared_ptr<Person> sp2(sp1);
    shared_ptr<Person> sp3;
    sp3 = sp1;  // 对象赋值
    // 2. 允许对象移动拷贝、移动赋值
    shared_ptr<Person> sp4 = move(sp3);
    shared_ptr<Person> sp5;
    sp5 = move(sp1);  // 移动赋值
    cout << "sp1:" << sp1.get() << endl;  // 已被移动
    cout << "sp2:" << sp2.get() << endl;
    cout << "sp3:" << sp3.get() << endl;  // 已被移动
    cout << "sp4:" << sp4.get() << endl;
    cout << "sp5:" << sp5.get() << endl;
    // 允许存储到容器中
    vector<shared_ptr<Person>> vec;
    vec.push_back(sp2);
    vec.push_back(sp4);
    vec.push_back(sp5);
}
int main()
{
    test();
    return 0;
}

3.引用计数

多个 shared_ptr 管理同一个 Person 对象,当某个 shared_ptr 生命周期结束,不会导致其他的 shared_ptr 管理了无效指针吗?

答案是不会的。这是因为 shared_ptr 是通过引用计数的方式来实现对象共享的。那么,什么是引用计数呢?

在 shared_ptr 对象的内部维护了两个非常重要的内容:

  1. 动态创建对象
  2. 引用计数对象
#include <iostream>
using namespace std;
struct Person
{
    Person()
    {
        cout << "Person 构造函数" << endl;
    }
    ~Person()
    {
        cout << "Person 析构函数" << endl;
    }
};
void test()
{
    // 初始化智能指针,引用计数为 0
    shared_ptr<Person> sp1(new Person);
    cout << "sp1:" << sp1.use_count() << endl;
    
    // 发生拷贝,引用计数 +1
    shared_ptr<Person> sp2(sp1);
    cout << "sp2:" << sp2.use_count() << endl;
    // 发生赋值,引用计数 + 1
    shared_ptr<Person> sp3;
    sp3 = sp2;
    cout << "sp3:" << sp3.use_count() << endl;
    
    // 判断是否独占资源
    cout << "sp1 是否独占资源:" << sp1.unique() << endl;
    // sp2 释放资源所有权,通过该对方访问的引用次数为 0
    sp2.reset();
    cout << "sp2:" << sp2.use_count() << endl;
    // sp1 和 sp2 引用计数为 2
    cout << "sp1:" << sp1.use_count() << endl;
    cout << "sp3:" << sp3.use_count() << endl;
}
int main()
{
    test();
    return 0;
}

共享指针通常运行在多线程程序中,其中多个指针可能会从不同线程更新同一个引用计数器,计数器是以原子方式实现,或者使用互斥锁来防止数据竞争。引用计数是原子安全的。

引用计数什么时候会发生变化

  1. 每次调用共享对象的复制构造函数时,共享指针都会递增计数器。
  2. 每次调用共享对象的赋值运算符时,右侧指针都会递增其计数器,左侧指针会递减。
  3. 每次调用共享对象的析构函数时,都会减少其计数器。
  4. 计数器为0时,则删除该对象。

4.自定义删除器

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <memory>
using namespace std;
void my_deleter(FILE* fp)
{
    cout << "文件自动关闭" << endl;
    fclose(fp);
    fp = nullptr;
}
struct MyDeleter
{
    void operator()(FILE* fp)
    {
        cout << "文件自动关闭" << endl;
        fclose(fp);
        fp = nullptr;
    }
};
void test()
{
    // 1. 使用普通函数作为自定义删除器
    shared_ptr<FILE> sp1(fopen("./demo.txt", "w"), my_deleter);
    // 2. 使用函数对象作为自定义删除器
    shared_ptr<FILE> sp2(fopen("./demo.txt", "w"), MyDeleter());
    // 3. 使用 lambda 匿名函数对象作为自定义删除器
    shared_ptr<FILE> sp3(fopen("./demo.txt", "w"), [](FILE* fp) {
            cout << "文件自动关闭" << endl;
            fclose(fp);
            fp = nullptr;
        });
}
int main()
{
    test();
    return 0;
}

4,weak_ptr

std::weak_ptr 是 C++ 标准库中的一个智能指针类,用于解决 std::shared_ptr 可能引发的循环引用问题。循环引用可能导致内存泄漏,因为引用计数无法降为零,从而无法释放对象。

std::weak_ptr是一种弱引用,它允许你观测由 std::shared_ptr 管理的对象,但不会增加对象的引用计数。换句话说,std::weak_ptr 不拥有所指向对象的所有权,因此不会影响对象的生命周期。当 std::shared_ptr 管理的对象被销毁后,对应的 std::weak_ptr 会自动失效,指向空值

1.用法

weak_ptr 是作为 shared_ptr 辅助角色存在,不会被直接用来管理动态对象。所以:

  1. 不会直接创建 weak_ptr 去管理动态对象
  2. weak_ptr 只能通过 shared_ptr 对象创建
  3. weak_ptr 引用 shared_ptr 对象时,并不会增加引用计数
  4. weak_ptr 不直接操作 shared_ptr 管理的对象,但允许间接操作 shared_ptr 管理的对象
#include <iostream>
using namespace std;
class Person
{
public:
    Person(int, int)
    {
        cout << "构造函数" << endl;
    }
    void show()
    {
        cout << "Person::show 函数" << endl;
    }
    ~Person()
    {
        cout << "析构函数" << endl;
    }
};
void test()
{
    // weak_ptr 是对 shared_ptr 的辅助,其自身并不拥有资源所有权
    shared_ptr<Person> sp1 = make_shared<Person>(10, 20);
    
    // 通过拷贝 shared_ptr 对象创建 weak_ptr 对象
    weak_ptr<Person> wp1(sp1);
    // weak_ptr 使用时,不能直接访问对象成员
    // 必须使用 lock 方法返回一个 shared_ptr 对象(会增加引用)
    if (wp1.expired())
    {
        return;
    }
    //返回一个新的shared_ptr,会增加引用技术
    auto sp2 = wp1.lock();
    sp2->show();
    
    // 可以将 shared_ptr 赋值给 weak_ptr 对象
    weak_ptr<Person> wp2;
    wp2 = sp1;
    // weak_ptr 相当于一个不增加引用的 shared_ptr
    cout << "sp1:" << sp1.use_count() << endl;
    cout << "sp2:" << sp2.use_count() << endl;
    cout << "wp1:" << wp1.use_count() << endl;
    cout << "wp2:" << wp2.use_count() << endl;
}
int main()
{
    test();
    return 0;
}

2.循环引用

#include <iostream>
using namespace std;
// 双向链表节点
class LinkNode
{
public:
    LinkNode(int value)
    {
        cout << "LinkNode 构造函数" << endl;
        data = value;
    }
    ~LinkNode()
    {
        cout << "LinkNode 析构函数" << endl;
    }
public:
    int data;
    shared_ptr<LinkNode> prev;
    shared_ptr<LinkNode> next;
};
void test()
{
    // 创建两个链表节点
    shared_ptr<LinkNode> node1(new LinkNode(10));
    shared_ptr<LinkNode> node2(new LinkNode(20));
    // 建立节点之间的关系
    node1->next = node2;
    node2->prev = node1;
}
int main()
{
    test();
    return 0;
}

会发现析构函数没有被调用

  1. 由于循环引用,导致两个 LinkNode 关联的引用计数为 2
  2. 当 test 函数生命周期结束,node1 和 node2 的析构函数调用,两个 LinkNode 对象引用计数 -1,引用计数为 1
  3. 只有当 LinkNode 实际被销毁时,才会调用 prev 和 next 的析构函数

3.weak_ptr 使用示例

当一个对象持有对另一个对象的引用,而后者又持有对前者的引用时,就会产生循环引用。所以,要打破这种循环的话,可以使用在发生循环引用的地方使用弱引用

#include <iostream>
using namespace std;
// 双向链表节点
class LinkNode
{
public:
    LinkNode(int value)
    {
        cout << "LinkNode 构造函数" << endl;
        data = value;
    }
    ~LinkNode()
    {
        cout << "LinkNode 析构函数" << endl;
    }
public:
    int data;
    // 使用 weak_ptr 代替 shared_ptr
    weak_ptr<LinkNode> prev;
    weak_ptr<LinkNode> next;
};
void test()
{
    // 创建两个链表节点
    shared_ptr<LinkNode> node1(new LinkNode(10));
    shared_ptr<LinkNode> node2(new LinkNode(20));
    // 建立节点之间的关系
    node1->next = node2;
    node2->prev = node1;
}
int main()
{
    test();
    return 0;
}

  • 13
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值