c++primer 第十二章 动态内存

目录

12.1 动态内存与智能指针

12.1.1 shared_ptr类

12.1.2 直接管理动态内存

12.1.3 shared_ptr和new结合使用

12.1.4 智能指针和异常

12.1.5 unique_ptr

12.1.6 weak_ptr 

12.2 动态数组

12.2.1 new和数组

12.2.2 allocator类


12.1 动态内存与智能指针

        c++中,通过new运算符和delete来管理动态内存,新标准中提供了两种智能指针,智能指针与普通指针的区别在于,它负责自动释放所指向的对象,shared_ptr允许多个指针指向同一对象,unique_ptr则独占所指向的对象。该指针定义在memory头文件中

12.1.1 shared_ptr类

        (1)定义:同vector一样,定义时候需要指出指针所指向的对象

                shared_ptr<string> p1;

        (2) make_shared函数:

        此函数在动态内存分配中分配一个对象并且初始化它,返回指向此对象的shared_ptr。当要使用该函数时,必须指定想要创建的对象的类型。

                shared_ptr<int> p3=make_shared<int>(42)  //指向一个值为42的int的shared_ptr;

        (3)shared_ptr的拷贝和赋值

          进行拷贝或赋值时,每个shared_ptr会记录有多少个其他shared_ptr指向相同的对象,其内部存在一个引用计数,当用一个shared_ptr去初始化另一个shared_ptr时,其关联的计数会递增,当计数器为0时,通过析构函数自动销毁其所管理的对象。

        auto p=make_shared<int>(42);//p指向的对象只有p一个引用者

        auto q(p)//p和q指向相同对象,此对象有两个引用者,递增q所指向对象的引用计数

        我们认为每个shared_ptr都有一个关联的计数器,通常称为引用计数,当我们拷贝一个shared_ptr时,计数器会递增。

        一旦一个shared_ptr的计数器为0,他就会自动释放所指向的对象。

12.1.2 直接管理动态内存

        (1)使用new动态分配内存和初始化对象

                int *pi=new int;

                自由空间分配的内存是无名且默认初始化的,返回指向该对象的指针,可以直接初始化的方式来初始化一个动态内存对象

                int *pi=new int(42)//pi指向对象的值为42,若()内为空,则默认初始化为0

                const int *pci=new int(12)//合法,且动态分配的const对象必须初始化,返回一个指向const的指针

             (2)delete

                delete表达式接受一个指针,释放所指向的对象。传递给delete的指针必须指向动态分配的内存,或者是一个空指针,释放一块非new分配的内存或者将相同的指针值释放多次,其行为是未定义的。

                delete一个指针后,指针值变为无效,变为空悬指针,即指向一块曾经保存数据对象但现在已经无效的内存的指针。可将nullptr赋与该指针避免空悬指针问题。

        (3)动态分配的const对象

                用new分配const对象是合法的:

                const int *p=new const int (1024);

                一个动态分配的const对象必须进行初始化

        (4)new若无法分配所要求的空间,会抛出一个类型为bad_alloc的异常


12.1.3 shared_ptr和new结合使用

         若不初始化一个智能指,其就会被初始化为一个空指针,可以使用new返回的指针来初始化智能指针,使用方法:shared_ptr<int> p2(new int(42))//p2指向一个值为42的int

        必须将shared_ptr显示绑定到一个想要返回的指针上

shared_ptr<int> clone(int p)
{
    return new int (p);//错误
}
shared_ptr<int> clone(int p)
{
    return shared_ptr<int>(new int(p));//正确
}

        默认情况下,一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针默认使用delete释放对象。

        (1)不要混合使用智能指针和普通指针

void process(shared_ptr<int> ptr)
{
    //使用ptr
}//ptr离开作用域

            传值方式传递给ptr,拷贝一个shared_ptr会递增其引用计数,因此,上述函数运行时,引用计数至少为2,当process结束时,ptr的引用计数会递减,但不会变为0,因此,当局部变量ptr被销毁时,其指向的内存不会被销毁

shared_ptr<int> p(new int(1024))
process(p);
int i=*p//正确,引用计数值为1


int *x(new int(1024))   //x为一个普通指针
process(x)  //错误,x不能转化为shared_ptr
process(shared_ptr<int>(x));   //合法,但内存会被释放
int j=*x    //错误,x为空悬指针
               

12.1.4 智能指针和异常

void f()
{
    shared_ptr<int> sp(new int(42));
    //这段代码抛出一个异常,且在f中被捕获,sp也会被销毁
}//函数结束时sp也会被销毁
void f()
{
    int *ip=new int(42);
    //代码捕获异常,则ip不会被释放
    delete ip;
}//若函数能正常结束,ip才会被释放

12.1.5 unique_ptr

        定义:某个时刻只能指向一个给定的对象,被销毁时,所指定的对象也会被销毁

       定义一个unique_ptr时,必须将其绑定到一个new 返回的指针上, 初始化时必须采用直接初始化形式

        unique_ptr<int> p2(new int(42));

        不支持拷贝和赋值操作,但是有一种情况例外,可以拷贝一个将要被销毁的unique_ptr,常见例子为函数返回。传递unique_ptr参数和返回unique_ptr

unique_ptr<int> clone(int p)
{
    return unique_ptr<int>(new int(p));//正确,从int*创建一个unique_ptr
}

12.1.6 weak_ptr 

        (1)定义:weak_ptr是一种不控制所指向对象生存周期的智能指针,指向一个shared_ptr管理的对象,将weak_ptr绑定到shared_ptr不会改变shared_ptr引用计数,一旦最后一个指向对象的shared_ptr被释放,则weak_ptr也会被释放。

        创建一个weak_ptr时需要一个shared_ptr来初始化他。

auto p=make_shared<int>(42);
weak_ptr<int> wp(p);

        对象可能不存在,所以不能通过weak_ptr直接访问对象,必须调用lock。此函数检查weak_ptr指向对象是否存在,如果存在,返回一个指向共享对象的shared_ptr。

if(shared_ptr<int> np=wp.lock())     //如果np不为空则条件成立,即lock返回true
{
}

12.2 动态数组

12.2.1 new和数组

        int *p=new int[size]  //new 返回的是一个元素类型的指针

        int *pi=new int[size]()  //默认初始化,值均为0

        int *p=new int[2]{1,2};

        删除:

        delete [] p;

     (1)分配空数组是合法的   

size n=get_size();
int *p=new int[n];
for(int *q=p;q!=p+n;++q)

        若n为0,上述定义也是合法的,new分配一个大小为0的数组,返回一个合法的非空指针,此指针就像尾后指针一样。可以像使用尾后迭代器一样使用此指针。但此指针不能解引用-因为它不指向任何元素。上述for循环会失败,因为p=q+n,因为n为0,因此循环不会被执行。

       (2)使用unique_ptr管理动态数组

unique_ptr<int []> up (new int[10]);//up指向一个int数组
up.release();//自动调用delete[]销毁指针

可以使用下标运算符访问数组中的元素
for(int i=0;i<10;i++)
    up[i]=i;

               shared_ptr不支持管理动态数组,如果想要使用,必须定义自己的删除器。

12.2.2 allocator类

        该类定义在头文件memory中,将内存分配和对象构造分离开来,当一个allocator对象分配内存时,他会根据给定的对象类型和确定恰当的内存大小和对其位置。

        (1)定义:

allocator<string> alloc;               //可以分配string的allocator对象
auto const p=alloc.allocate(n);        //分配n个未初始化的string

         (2)allocator分配未构造的内存

        allocator分配的内存是未构造的,construct成员函数接受一个指针和零个或多个额外参数,在给定位置构造一个元素,额外参数用来初始化构造的对象.

alloc.construct(p,"hi")

         使用完对象后,必须对每个构造的元素调用destroy来销毁。函数destroy接收一个指针,对指向的对象执行析构函数

                alloc.destroy(q);        //释放真正构造的string

                alloc.deallocate(p,n);   //释放内存

例:将vector中的元素拷贝过来,分配一个大两倍的空间,后面的用以定值填充。

vector<int> v = { 1,2,3,4,5,6 };
allocator<int> alloc;
auto p = alloc.allocate(v.size() * 2);
auto q = uninitialized_copy(v.begin(), v.end(), p);
uninitialized_fill_n(q, v.size(), 10);

    

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值