c++笔记-动态内存(new)

  1. 相对于智能指针来说,运算符new和delete的直接管理内存使用不当会容易出错。
  2. 使用new来动态分配和初始化对象
//由于在自由空间分配到的内存是无名的,因此new无法为其分配的空间命名
//而是返回一个指向该对象的指针
int *pi = new int;  //pi指向一个动态分配的、未初始化的无名int类型对象
//可以采用直接初始化
int *pi = new int(1024);    //pi指向的对象的值为1024
string *ps = new string(10,'9');    //ps指向字符串"99999999999",*ps为"9999999999"
//也可以采用值初始化,注意区分值初始化和默认初始化
string *p1 = new string;    //默认初始化为空string
string *p2 = new string();  //值初始化为空string
int *pi1 = new int;     //默认初始化:*pi1的值未定义
int *pi2 = new int();   //值初始化:*pi2的值为0
#注意#
对于自己定义了构造函数的类的类型,要求值初始化是无意义的,因为无论如何都会通过默认构造函数来初始化。
但对于内置的类型,值初始化的内置类型对象有着良好的定义的值

使用new分配const对象是合法的
//分配并初始化一个const int
const int *pci = new const int(1024);
//分配并默认初始化一个const的空string
const string *pcs = new const string;
类似其他任何const对象,一个动态分配的const对象必须进行初始化。
  1. 当自由空间被耗尽时,new表达式就会失败,默认情况下会抛出一个类型为bad_alloc的异常,bad_alloc和nothrow都有定义在new头文件中。
可以通过改变使用new的方式来阻止抛出异常
//如果分配失败,new返回一个空指针
int *p1 = new int;  //如果分配失败,则抛出std::bad_alloc异常
int *p2 = new (nothrow) int;    //如果分配失败,new返回一个空指针
//我们称这种new称为定位new。
  1. delete释放内存。delete接受一个指针,指向我们想要释放的对象。该指针必须指向动态分配的内存,或者时一个空指针。
如果释放一块并非new分配的内存,或者将相同的指针值释放多次,其行为是未定义的
int i, *pi1 = &i, *pi2 = nullptr;
double *pd = new double(33), *pd2 = pd;
//现在进行如下操作
delete i;   //错误,i不是一个指针
delete pi1; //未定义:pi1指向的是一个局部变量,不是动态内存分配
delete pi2; //正确:释放一个空指针
delete pd;  //正确
delete pd2; //未定义:pd2指向的内存在上一步已经释放了
//对于const对象,其值不能改变,但是对象可以被销毁
const int *pci = new const int(1024);
delete pci; //正确:释放一个const对象
  1. 关于动态对象的生存周期问题
当返回指向动态内存的指针(而不是智能指针)的函数,其使用者必须记得释放内存
//factory返回一个指针,指向一个动态分配的对象
Foo* factory(T arg)
{
    return new Foo(arg);    //调用者负责释放此内存
}
//当出现以下使用方式时
void use_factory(T arg)
{
    Foo *p = factory(arg);
    //使用p但不delete它
}
//p离开了作用域,但是此时它所指向的内存没有得到释放
//由内置指针(而不是智能指针)管理动态内存,在被显示释放(delete)之前一直都会存在
在此例中一旦use_factory返回了,就无法释放内存
//因此,在返回前释放
void  use_factory(T arg)
{
    Foo *p = factory(arg);
    //使用p
    delete p;
}
//还有一种情况,就是当我们程序中其他代码需要使用到use_factory分配的对象时
//即函数返回一个指针,指向所分配的对象
Foo *use_factory(T arg)
{
    Foo *p = factory(arg);
    //使用p
    return p;
}
//此时,调用者必须在其使用后delete它
  1. 空悬指针,指的是当指针delete后,指针值变得无效。在有些机器上指针中仍然保存着原来(释放了)的地址。但是该地址已经没有对象存储。为了避免该情况出现,可以在delete后,显示的将nullptr赋值给指针。也需要注意的是,这种赋值只对当前赋值的指针生效,对指向相同地址的其他指针无效。
int *p(new int(42));    //p指向动态内存
auto q = p; //p和q指向相同的内存
delete p;   //释放p所指向的内存。p和q都变为无效
p = nullptr;    //指出p不再绑定到其他对象上
//p避免了空悬指针的出现,但是指向同样内存地址的q就还是可能出现问题
//在实际应用中,很难找到所有指向同一内存地址的指针
  1. shared_ptr和new的结合使用
使用时需注意:必须使用直接初始化来初始化一个智能指针
shared_prt<int> pi = new int(1024);//错误用法:必须使用直接初始化形式
shared_ptr<int> pi(new int(1024));//正确用法:使用了直接初始化

//对于函数同样
shared_ptr<int> clone(int p)
{
    return new int(p);  //错误:不能隐式转换为shared_ptr<int>
}

shared_ptr<int> clone(int p)
{
    return shared_ptr<int>(new int(p)); //必须显示的用int*创建shared_ptr<int>
}

当我们对函数使用值传递方式,就算shared_ptr类型也会拷贝一个副本
这就意味着增加了引用次数2此,当函数返回,局部变量销毁后,shared_ptr指向的内存不会被释放
//在函数调用时ptr被创建并初始化
void process(shared_ptr<int> ptr)
{
    //使用ptr
}//ptr离开作用域,被销毁
但是,由于是值传递,因此会在调用函数时,拷贝一个shared_ptr。
当process结束时,ptr的引用不会减为零,其指向的内存不会被释放。
正确的操作是给函数传递一个shared_ptr临时对象
shared_ptr<int> p(new int(1024));   //使用直接初始化
process(p);     //这将会拷贝p;在process中,p的引用次数为2
int i = *p;     //正确操作:p的引用数为1

int *x(new int(1024));  //x为一个普通指针
process(x);     //错误:不能将普通指针转换为智能指针

process(shared_ptr<int>(x));    //创建一个临时变量,在函数执行完后,对象会被销毁和释放
int j = *x;     //未定义:x为空悬指针,所指向的对象被销毁
  1. 不要使用shared_ptr中到的get成员函数来初始化一个智能指针或为智能指针赋值。get函数返回一个内置指针,指向智能指针所管理的对象,当用来初始化或赋值给其他指针时,其他指针被释放后,该对象原来的指针就会变为空悬指针。
  2. 智能指针在程序返回或者遇到异常时,局部shared_ptr对象都会被销毁并释放内存。当使用new来分配新对象时,在程序抛出异常,如果异常未被捕获,同时在函数外没有使用释放该新对象,则这块内存无法被释放。
  3. 智能指针使用陷阱
不使用相同的内置指针值初始化(或reset)多个智能指针

不delete由get()返回的指针

不使用get()或者reset另一个指针

如果使用了智能指针管理的资源不是new分配的内存,记得传递给它一个删除器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值