C++:33---动态内存管理new、delete
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了C++:33---动态内存管理new、delete,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含6134字,纯文字阅读大概需要9分钟。
内容图文
相对于智能指针,这两个运算符管理内存非常容易出错
一、new关键字
- new用来在内存中分配一块内存,new分配的对象是没有名称的,而是返回一个指向该对象的指针
int *p1=new int(1); //pi指向一个动态分配的、初始化值为1的无名对象
int *p2(new int(1)); //同上
二、new的值初始化规则
通用规则:
- 如果类型名后无括号:内置类型或组合类型的对象的值是未定义的,而类类型对象江永默认构造函数进行初始化
- 如果类型名后有括号:则内置类型/组合类型/类类型都进行默认的初始化
//如果类型名后无括号 string *ps = new string; //ps为初始化为空的string cout << *ps << endl; //值为空 int *pi = new int; //pi指向一个未初始化的int cout <<*pi << endl; //值为乱值
//如果类型名后有括号 string *ps = new string(); //ps为初始化为空的string cout << *ps << endl; //值为空 int *pi = new int(); //pi指向一个默认初始化的int cout <<*pi << endl; //值为0
C++11标准下的初始化规则:
- 允许使用花括号来制定初始化列表
- 注意:花括号{}本质上是用来当一个列表的
vector<int> *vec = new vector<int>{ 1,2,3,4,5 }; int *arr = new int[3]{1,2,3}; int *p = new int{ 1 };
auto初始化:
- 如果使用圆括号初始化器,就可以使用auto来腿短我们想要分配的对象类型。但是花括号不行
auto p = new auto(10); //p是一个int*指针 auto p2 = new auto("Hello"); //p2是一个string*指针 cout <<*p <<*p2<< endl; //输出10和Hello auto* p = new auto(10); //p同上 auto* p2 = new auto("Hello"); //p2同上 cout <<*p <<*p2<< endl; //输出10和Hello
auto p2 = new auto{1};//错误 auto p3 = new auto{1, 2, 3};//错误
const初始化
因为const对象为常量,初始化之后就不可以修改值了
- 类类型初始化时可以不给出值,此时使用默认的构造函数值
- 其它类型必须显示地给出初始化值(注意:编译器本质允许不给出值,但是定义之后就不能改变值了)
const int *p = new const int;//*p为乱值,且*p不允许修改了 const string *p2 = new const string;//使用string默认构造,*p2为空的且不允许修改 //建议做法: const int *p = new const int(10);//给出初始化值
三、bad_alloc异常处理
- bad_alloc异常出现的情景:如果一个程序可用的内存消耗完,那么new表达式就会失败。默认情况下,此时会抛出bad_alloc异常
- bad_alloc异常的处理:如果不处理此异常那么程序就会中断。但是我们可以使用定位new的nothrow关键字来处理此异常
- nothrow关键字:如果在new后面加一个圆括号并且加上“nothrow”,那么捕获到bad_alloc异常时,new返回空指针,而不抛出异常。我们称这种形式的new为“定位new”
- bad_alloc和nothrow都定义在头文件new中
//如果此时内存不足:
int *p=new int; //抛出std::bad_alloc异常,程序终止
int *p2=new(nothrow) int; //new返回一个空指针,p2为空
四、delete关键字
- 用来释放一块动态申请的内存,解除指针与该指针所指向的内存之间的关系
- 如果new的动态内存没有被释放(销毁),那么该动态内存就一直存在,会造成浪费
五、delete的使用规则
规则如下:
- 不能用来释放一块静态内存(栈区)
- 用来释放动态申请的内存(new申请的堆区)
- 允许释放一个空指针,不会出错
- 释放一块已经释放的内存是错误的
- 虽然const对象的值不能被改变,但是可以使用一个const动态对象
int i=10;
int *p=&i;
int *p2=nullptr;
double *pd=new double(3.14);
double *pd2=pd;
delete i; //错误,i不是一个指针
delete p; //错误;p是一个指针,指向的堆区的内存,而不是动态申请的
delete p2 //正确,释放空指针是允许的
delete pd; //正确,释放一个动态内存
delete pd2 //错误,pd指向的内存已经被释放了,不能再重复释放
const int *p=new const int(30);
delete p; //正确
六、内存泄漏问题
- 当我们使用new申请一块动态内存后,如果没有delete掉内存,那么就会造成内存泄漏
案例:
定义一个factory函数,返回一个指向与Foo类型的动态内存指针
Foo* factory(T arg) { ... return new Foo(arg); }
- 错误使用:下面的函数,调用了factory函数申请了一块动态内存,但是函数结束之后,没有释放p所指向的内存,于是就造成了内存的浪费
void use_factory(T arg) { Foo *p=factory(arg); }
- 正确使用:下面对use_factory函数进行了改造,在函数的最后delete掉了p所指向的动态内存,这样就不会导致内存的泄漏了
void use_factory(Foo arg) { Foo *p=factoyr(arg); ... delete p; }
七、delete指针之后的置空问题
- 规则:当我们释放一个指针之后,该指针指向的是一个不确定的内存空间。因此,当释放指针之后,建议将指针值为空,来指示该指针不指向任何对象了
int *p=new int(30); //申请
......
delete p; //释放
p=nullptr; //置位空
八、多个指针同指一块内存的使用
特点:
- ①多个指针指向同一内存时,释放其中一个指针,其他指针均变为无效
- ②将一个指针值为空,置于该指针有关,与其他指针无关
int *p(new int(42));
auto q=p; //q与p指向同一块内存空间
delete p; //释放p之后,q指针也失效
p=nullptr; //将p置为空,但是q没有(两个指针没有任何关系)
九、shared_ptr与new的使用
使用规则:
- ①我们可以使用将shared_ptr类对象指向一个new所申请的动态内存
- ②new申请的动态内存的使用、释放等规则仍然符合shared_ptr类的使用规则
使用语法:
- 因为智能指针的构造函数时explicit的。因此:我们不能将一个内置指针隐式地转换为一个智能指针,必须使用直接初始化形式来初始化一个智能指针
shared_ptr<int> p=new int(1024); //错误 shared_ptr<int> p2(new int(1024)); //正确:使用直接初始化
- 动态内存作为返回值时的使用手法:限于上面的使用语法:一个返回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)); //正确 }
十、new传参时与shared_ptr的关系
当一个函数的参数是shared_ptr类时,有以下规则:
- 函数的调用是传值调用
- 调用函数时,该shared_ptr类所指向的对象引用计数加1。但是函数调用完成之后,shared_ptr类自动释放,对象的引用计数又减1
void process(shared_ptr<int> ptr){ ... }
shared_ptr<int> p(new int(42)); //初始化一个智能指针对象p
process(p); //p所指的对象引用计数加1
//process函数调用之后,p所指的引用计数减1
int i=*p; //正确
函数参数使用时与new的关系:
- 因为shared_ptr类会在生存周期结束之后,将引用计数减1,当引用计数为0时,会释放内存空间
下面是一个特殊的应用场景,需要注意
void process(shared_ptr<int> ptr){ ... } int *x(new int(1024)); process(x); //错误,不能将int*转换为一个shared_ptr<int> process(shared_ptr<int>(x)); //合法的,但是process函数返回之后内存会被释放 int j=*x; //错误,x所指的内存已经被释放了
十一、异常处理
当程序发生异常时,我们可以捕获异常来将资源被正确的释放。但是如果没有对异常进行处理,则有以下规则:
- shared_ptr的异常处理:如果程序发生异常,并且过早的结束了,那么智能指针也能确保在内存不再需要时将其释放
- new的异常处理:如果释放内存在异常终止之后,那么久造成内存浪费
voif func()
{
shared_ptr<int> sp(new int(42));
...//此时抛出异常,未捕获,函数终止
}//shared_ptr仍然会自动释放内存
voif func()
{
int *ip=new int(42);
...//此时抛出异常,未捕获
delete ip; //在退出之前释放内存,此语句没有执行到,导致内存浪费
}
内容总结
以上是互联网集市为您收集整理的C++:33---动态内存管理new、delete全部内容,希望文章能够帮你解决C++:33---动态内存管理new、delete所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。