• 第12章 动态内存


    全局对象:启动时分配,结束时销毁

    局部对象:程序块内分配,程序块外销毁

    static对象:第一次使用分配,结束时销毁

    动态内存使用new来分配对象,使用delete销毁对象

    12.1两种智能指针

    #include<memory>

    shared_ptr: 多个指针可以指向同一个对象

    unique_ptr: 独占指向的对象

            weak_ptr: 一个伴随类,指向shared_ptr管理的对象,是弱引用

    智能指针也是模板,定义时需要指明类型

    shared_ptr<string> p1;

    shared_ptr<list<int>> p2;

    默认初始化为空指针

    shared_ptr和unique_ptr都支持的操作

    unique_ptr<T> up

    shared_ptr<T> sp

    空智能指针,指向类型为T的对象

    p

    作为一个条件判断,指向一个对象则为true

    *p

    解引用,获得指向的对象

    p->item

    等价于(*p).item

    p.get()

    返回p中保存的指针

    swap(p, q)

    p.swap(q)

    交换p和q中的指针

    shared_ptr独有的操作

    make_shared<T>(args)

    返回一个shared_ptr,使用args初始化T的对象

    shared_ptr<T> p(q)

    p是q的拷贝,指向同一个对象,q中计数器递增

    p=q

    递减p中引用计数,递增q中引用计数

    p.unique()

    如果p.use_count()为1,返回true

    p.use_count()

    返回p共享对象的智能指针的数量,很慢,用于调试

    使用动态内存的三种原因

    1. 程序不知道需要使用多少对象
    2. 程序不知道所需对象的准确类型
    3. 程序需要在多个对象间共享数据

    12.1.2直接管理内存

    int *ptr = new int; //默认初始化,值未定义
    int *ptr1 = new int();//值初始化为0
    int *ptr2 = new int(1024); 
    const int *ptr3 = new const int(1024);
    int *ptr4 = new (nothrow) int(1024);//分配失败,不抛出bad_alloc异常,而是返回空指针
    string *str = new string; //默认初始化为空string
    string *str1 = new string();//值初始化为空string
    string *str2 = new string("string");
    vector<int> *pv = new vector<int>{ 1, 2, 3, 4, 5 };
    auto p = new auto("123");//为const char**
    auto p = new auto(string("123"));//为string*
    
    int i, *pi1 = &i, *pi2 = nullptr;
    double *pd = new double(33), *pd2 = pd;
    delete i;  // 错误,i不是指针
    delete pi1; // 未定义,pil是一个局部变量
    delete pd;  // 成功
    delete pd2; // 未定义,pd2已经被释放
    delete pi2; // 成功,释放了一个为空的指针

    释放内存之后,指针变成了悬空指针(dangling pointer),通常仍然指向原来的内存地址,如果之后再次delete,会破坏自由空间内存,通常需要重新赋予nullptr。

    12.1.3结合使用shared_ptr和new

    shared_ptr<int> p2 = new int(42);// 错误int*不能隐式转换成智能指针
    shared_ptr<int> p2(new int(42));// 正确,因为构造函数是explicit,上边才不成立
    int* q = new int(10);
    shared_ptr<int> p(q, [](int* p) {cout << *p; delete p; });// 自定义释放内存的方法

    不建议结合使用

    其他操作

    shared_ptr<int> p(new int(9)); 
    int *pi = p.get();// 得到内置指针
    if (!p.unique())//不是唯一引用
        p.reset(new int(*p + 1));//改变p的指向,不影响其他引用的值
    *p = *p + 1;//唯一引用,直接改变也不影响其他的了

    12.1.4智能指针与.Net using

    void f(destination &d)
    {
        connection c = connect(&d);
        //保证在程序结束后释放c
        shared_ptr<connection> p(&c, [](auto c) {delete c; });
        ...
    }

    12.1.5独占智能指针unique_ptr

    unique_ptr不能进行赋值、拷贝操作

    unique_ptr<int> u;
    u = nullptr;//释放u的对象,并置空
    u.release();//释放u的对象控制,并返回内置指针,置空u
    u.reset();//释放u的对象
    u.reset(q);//指向q,释放u的对象

    可以拷贝和返回一个将要被销毁的unique_ptr例如参数返回时的拷贝,这其实是一种特殊的拷贝(13.6.2)。

    自定义删除器需要在定义时指明类型,这与shared_ptr作为参数不同。

    unique_ptr<int, decltype(process)*> u(new int(10), process);

    12.1.6弱智能指针weak_ptr

    weak_ptr将会绑定到一个shared_ptr,它不会改变shared_ptr的引用计数。

    弱智能指针需要用shared_ptr对象初始化。

    shared_ptr<int> u1(new int(50));
    weak_ptr<int> w = u1;
    w.reset();// 置空w
    w.use_count();//共享shared_ptr的数量
    w.expired();//user_count为0,返回true,否则为false
    w.lock();//返回对应的一个shared_ptr对象,如果expired为true,则返回的是空的对象
    if (shared_ptr<int> u = w.lock())//可以判断并得到shared_ptr

    12.2动态数组

    最好使用vector、string等其他标准库中的容器,这些标准库可以使用默认版本的拷贝、赋值和析构操作,而使用动态数组,就需要自己考虑了。

    12.2.1数组的new

    int *pia = new int[10];//10个未初始化
    int *pia1 = new int[10]();//10个值初始化为0
    int *pia2 = new int[10]{ 1,2,3 };//前三个为1,2,3,其他的为0
    1. 需要注意的是,分配的内存空间并不是数组类型空间,而仅仅是内存空间,并且返回一个首地址。
    2. 使用new分配的对象,执行默认初始化。

    释放时需要用到

    delete[] pia;

    对于释放时delete中如何知道内存中的大小,一般的编译器是通过在分配的动态数组前记录分配的内存的大小,然后释放的时候读取记录进行释放。

    使用智能指针管理动态数组

    unique_ptr<int[]> u(new int[10]);
    cout << u[10] << endl;
    u.release();//可以自动调用delete []
    
    shared_ptr<int> sp(new int[10], [](int*p) {delete[] p; });//需要自定义销毁函数
    sp.reset();//使用自定义销毁函数

    shared_ptr不直接支持动态数组管理,所以也不支持下标运算和指针的算术运算,需要使用时,必须使用get获取内置指针。

    allocator<T> a

    定义一个为T类型对象分配内存的allocator对象

    a.allocate(n)

    为n个T类型对象分配内存

    a.deallocate(p, n)

    收回为n个T类型对象分配的内存

    a.construct(p, args)

    在p指向的位置构造T对象(需要一个一个构造)

    a.destroy(p)

    销毁p指向位置的T对象(需要一个一个销毁)

    int count = 10;
    allocator<string> a;
    //分配内存
    string* p = a.allocate(count);
    for (int i = 0; i < count; i++)
        a.construct(p + i);//构造对象
    for (int i = 0; i < count; i++)
        a.destroy(p + i);//销毁对象
    //收回内存
    a.deallocate(p, count);

    uninitialized_copy(b,e,b2)

    从迭代器b到e的范围中的对象,内拷贝到b2内存中

    uninitialized_copy_n(b,n,b2)

    从迭代器b开始的n个元素拷贝到b2内存中

    uninitialized_fill(b,e,t)

    在b到e的范围中创建t的拷贝

    uninitialized_fill_n(b,n,t)

    在b开始的n个元素内存中,创建t的拷贝

  • 相关阅读:
    格式与布局
    iframe
    tp
    头信息
    php 文件下载
    socket
    Flex 布局2
    Flex 布局
    下拉刷新
    选取一种类中含有某一属性值得元素的集合
  • 原文地址:https://www.cnblogs.com/qiusuo/p/5005727.html
Copyright © 2020-2023  润新知