动态内存与智能指针(2)
直接管理内存
void fun1() { //此new表达式在*空间构造一个int型对象,并返回指向该对象的指针 int *pi1=new int; //pi指向一个动态分配、未初始化的无名对象 string *ps3=new string; //初始化为空string int *pi2=new int; //pi指向一个未初始化的int int *pi3=new int(1024); //pi指向的对象的值为1024 string *ps4=new string(10, '9'); //*ps为“999999999” //vector有10个元素,值依次从0到9 vector<int> *pv=new vector<int>{0,1,2,3,4,5,6,7,8,9}; string *ps1=new string; //默认初始化为空string string *ps=new string(); //值初始化为空string int *pi4=new int; //默认初始化;*pi1的值未定义 int *pi5=new int(); //值初始化为0;*pi2为0 }
由于编译器要用初始化器的类型来推断要分配的类型,只有当括号中仅有单一初始化器
时才可以使用auto
void fun2() { int obj; auto p1=new auto(obj); //p指向一个与obj类型相同的对象 //该对象用obj进行初始化 // auto p2=new auto{a,b,c}; //错误:括号中只能有单个初始化器 }
动态分配的const对象
void fun3() { //用new分配const对象是合法的: //分配并初始化一个const int const int *pci=new const int(1024); //分配并默认初始化一个const的空string const string *pcs=new const string; }
而其他类型的对象就必须显示初始化,由于分配的对象是const的,new返回
的指针是一个指向const的指针
内存耗尽
一旦一个程序用光了它所有可用的内存,new表达式就会失败。默认情况下,如果new
不能分配所要求的内存空间,他就会抛出一个类型为bad_alloc的异常,我们可以使用
new的方式来阻止它抛出异常
不能分配所要求的内存空间,他就会抛出一个类型为bad_alloc的异常,我们可以使用
new的方式来阻止它抛出异常
void fun4() { //如果分配失败,new返回一个空指针 int *p1=new int; //如果分配失败,new抛出std::bad_alloc int *p2=new (nothrow) int; //如果分配失败,new返回一个空指针 //我们称这种形式的new为定位new,bad_alloc和nothrow都保存在头文件new中 }
释放动态内存
void fun5() { int *p=nullptr; delete p; //p必须指向一个动态分配的对象或是一个空指针 }
指针值和delete
释放一块并非new分配内存,或者将相同的指针值释放多次,其行为是未定义的
void fun6() { int i, *pi1=&i, *pi2=nullptr; double *pd=new double(33), *pd2=pd; delete i; //错误i不是一个指针 delete pi1; //未定义 delete pd; //正确 delete pd2; //未定义:pd2指向的内存已经被释放了 delete pi2; //正确:释放一个空指针总是没有错误的 //虽然一个const对象的值不能被改变,但它本身是可以被销毁的 const int *pci=new const int(1024); delete pci; //正确:释放一个const对象 }
动态对象的生存期直到被释放时为止
对于一个由内置指针管理的动态对象,直到被显式释放之前它都是存在的
typedef int T; struct Foo { // members are public by default Foo(T t): val(t) { } T val; }; //factory返回一个指针,指向一个动态分配对象 Foo* factory(T arg) { //视情况处理arg return new Foo(arg); //调用者负责释放此内存 } void use_factory1(T arg) { Foo *p=factory(arg); //使用p但不delete它 } //p离开它的作用域,但是他所指向的内存没有释放! //由内置指针管理的动态内存再被显式释放前一直都会存在。 void use_factory2(T arg) { Foo *p=factory(arg); //使用p而且delete它 delete p; }
当有两个指针指向相同的动态分配对象的时候,可能发生这种错误
如果对其中一个指针做了delete操作,对象的内存就返回给*空间了
如果我们delete第二个指针,*空间就可能被破坏。
坚持使用智能指针就能避免这些问题
delete后重置指针值
在delete之后指针就变成了我们所说的悬空指针,即指向一块曾经保存数据对象但现在
已经无效的内存指针
在指针将要离开其作用域之前释放掉它关联的内存
如果我们需要保留指针,可以再delete之后将nullptr赋予指针
这样就清楚地指出指针不指向任何对象
在指针将要离开其作用域之前释放掉它关联的内存
如果我们需要保留指针,可以再delete之后将nullptr赋予指针
这样就清楚地指出指针不指向任何对象
。。。这只是提供了有限的保护
void fun7() { int *p(new int(42)); //p指向动态内存 auto q=p; //p和q指向相同的内存 delete p; //p和q均变成无效 p=nullptr; //指出p不再绑定到任何对象 }
课后习题来一发!!!
/**
* 编写函数,返回一个动态分配的int的vector.将vector传递给另一个函数,
* 这个函数读取标准输入,将读入的值保存在vector元素中。再将vector
* 传递给另一个函数,打印读入值。记得在恰当地时候delete vector
*/
* 编写函数,返回一个动态分配的int的vector.将vector传递给另一个函数,
* 这个函数读取标准输入,将读入的值保存在vector元素中。再将vector
* 传递给另一个函数,打印读入值。记得在恰当地时候delete vector
*/
vector<int>* getVector() { vector<int> *pv=new vector<int>{1,2,3,4,5,6,7,8,9}; return pv; } void fun8(vector<int> *pv) { int i; //读入元素 while(cin>>i) { pv->push_back(i); } } void show(vector<int> *pv) { for(vector<int>::const_iterator it=pv->cbegin() ; it != pv->cend() ; ++it) { cout<<*it<<"\t"; } delete pv; }
使用shared_ptr写
//使用shared_ptr书写 shared_ptr<vector<int>> get_vector() { return shared_ptr<vector<int>>(new vector<int>{1,2,3,4,5,6,7,8,9}); } void addVector(shared_ptr<vector<int>> pv) { int i; //要添加的元素 while(cin>>i) { vector<int> *v=pv.get(); v->push_back(i); } } void showShared_ptr(shared_ptr<vector<int>> pv) { //取出shared_ptr里面的对象指针 vector<int> *v=pv.get(); //输出vector里面的数据 for(vector<int>::const_iterator it=v->cbegin() ; it != v->cend() ; ++it) { cout<<*it<<"\t"; } }
shared_ptr和new结合使用
void fun9() { shared_ptr<double> p1; //shared_ptr可以指向一个double shared_ptr<int> p2(new int(42)); //p2指向一个值为42的int // shared_ptr<int> p1=new int(1024); //错误:必须使用直接初始化形式 shared_ptr<int> p21(new int(1024)); //正确:使用直接初始化形式 } shared_ptr<int> clone(int p) { //正确:显示使用int*创建shared_ptr<int> return shared_ptr<int>(new int(p)); }
全部代码输出!
/** * 功能:动态内存与智能指针 * 时间:2014年7月7日16:41:57 * 作者:cutter_point */ #include<iostream> #include<string> #include<vector> #include<new> #include<memory> using namespace std; void fun1() { //此new表达式在*空间构造一个int型对象,并返回指向该对象的指针 int *pi1=new int; //pi指向一个动态分配、未初始化的无名对象 string *ps3=new string; //初始化为空string int *pi2=new int; //pi指向一个未初始化的int int *pi3=new int(1024); //pi指向的对象的值为1024 string *ps4=new string(10, '9'); //*ps为“999999999” //vector有10个元素,值依次从0到9 vector<int> *pv=new vector<int>{0,1,2,3,4,5,6,7,8,9}; string *ps1=new string; //默认初始化为空string string *ps=new string(); //值初始化为空string int *pi4=new int; //默认初始化;*pi1的值未定义 int *pi5=new int(); //值初始化为0;*pi2为0 } //由于编译器要用初始化器的类型来推断要分配的类型,只有当括号中仅有单一初始化器 //时才可以使用auto void fun2() { int obj; auto p1=new auto(obj); //p指向一个与obj类型相同的对象 //该对象用obj进行初始化 // auto p2=new auto{a,b,c}; //错误:括号中只能有单个初始化器 } //动态分配的const对象 void fun3() { //用new分配const对象是合法的: //分配并初始化一个const int const int *pci=new const int(1024); //分配并默认初始化一个const的空string const string *pcs=new const string; //对于一个定义了默认构造函数的类类型,其const动态对象可以隐式初始化, //而其他类型的对象就必须显示初始化,由于分配的对象是const的,new返回 //的指针是一个指向const的指针 } //内存耗尽 void fun4() { //一旦一个程序用光了它所有可用的内存,new表达式就会失败。默认情况下,如果new //不能分配所要求的内存空间,他就会抛出一个类型为bad_alloc的异常,我们可以使用 //new的方式来阻止它抛出异常 //如果分配失败,new返回一个空指针 int *p1=new int; //如果分配失败,new抛出std::bad_alloc int *p2=new (nothrow) int; //如果分配失败,new返回一个空指针 //我们称这种形式的new为定位new,bad_alloc和nothrow都保存在头文件new中 } //释放动态内存 void fun5() { int *p=nullptr; delete p; //p必须指向一个动态分配的对象或是一个空指针 } //指针值和delete //释放一块并非new分配内存,或者将相同的指针值释放多次,其行为是未定义的 void fun6() { int i, *pi1=&i, *pi2=nullptr; double *pd=new double(33), *pd2=pd; // delete i; //错误i不是一个指针 delete pi1; //未定义 delete pd; //正确 delete pd2; //未定义:pd2指向的内存已经被释放了 delete pi2; //正确:释放一个空指针总是没有错误的 //虽然一个const对象的值不能被改变,但它本身是可以被销毁的 const int *pci=new const int(1024); delete pci; //正确:释放一个const对象 } //动态对象的生存期直到被释放时为止 //对于一个由内置指针管理的动态对象,直到被显式释放之前它都是存在的 typedef int T; struct Foo { // members are public by default Foo(T t): val(t) { } T val; }; //factory返回一个指针,指向一个动态分配对象 Foo* factory(T arg) { //视情况处理arg return new Foo(arg); //调用者负责释放此内存 } void use_factory1(T arg) { Foo *p=factory(arg); //使用p但不delete它 } //p离开它的作用域,但是他所指向的内存没有释放! //由内置指针管理的动态内存再被显式释放前一直都会存在。 void use_factory2(T arg) { Foo *p=factory(arg); //使用p而且delete它 delete p; } /**当有两个指针指向相同的动态分配对象的时候,可能发生这种错误 * 如果对其中一个指针做了delete操作,对象的内存就返回给*空间了 * 如果我们delete第二个指针,*空间就可能被破坏。 * 坚持使用智能指针就能避免这些问题 */ //delete后重置指针值 /** * 在delete之后指针就变成了我们所说的悬空指针,即指向一块曾经保存数据对象但现在 * 已经无效的内存指针 * 在指针将要离开其作用域之前释放掉它关联的内存 * 如果我们需要保留指针,可以再delete之后将nullptr赋予指针 * 这样就清楚地指出指针不指向任何对象 */ //。。。这只是提供了有限的保护 void fun7() { int *p(new int(42)); //p指向动态内存 auto q=p; //p和q指向相同的内存 delete p; //p和q均变成无效 p=nullptr; //指出p不再绑定到任何对象 } /** * 编写函数,返回一个动态分配的int的vector.将vector传递给另一个函数, * 这个函数读取标准输入,将读入的值保存在vector元素中。再将vector * 传递给另一个函数,打印读入值。记得在恰当地时候delete vector */ vector<int>* getVector() { vector<int> *pv=new vector<int>{1,2,3,4,5,6,7,8,9}; return pv; } void fun8(vector<int> *pv) { int i; //读入元素 while(cin>>i) { pv->push_back(i); } } void show(vector<int> *pv) { for(vector<int>::const_iterator it=pv->cbegin() ; it != pv->cend() ; ++it) { cout<<*it<<"\t"; } delete pv; } //使用shared_ptr书写 shared_ptr<vector<int>> get_vector() { return shared_ptr<vector<int>>(new vector<int>{1,2,3,4,5,6,7,8,9}); } void addVector(shared_ptr<vector<int>> pv) { int i; //要添加的元素 while(cin>>i) { vector<int> *v=pv.get(); v->push_back(i); } } void showShared_ptr(shared_ptr<vector<int>> pv) { //取出shared_ptr里面的对象指针 vector<int> *v=pv.get(); //输出vector里面的数据 for(vector<int>::const_iterator it=v->cbegin() ; it != v->cend() ; ++it) { cout<<*it<<"\t"; } } //shared_ptr和new结合使用 void fun9() { shared_ptr<double> p1; //shared_ptr可以指向一个double shared_ptr<int> p2(new int(42)); //p2指向一个值为42的int // shared_ptr<int> p1=new int(1024); //错误:必须使用直接初始化形式 shared_ptr<int> p21(new int(1024)); //正确:使用直接初始化形式 } shared_ptr<int> clone(int p) { //正确:显示使用int*创建shared_ptr<int> return shared_ptr<int>(new int(p)); } int main() { /* vector<int> *pv=getVector(); fun8(pv); show(pv); */ shared_ptr<vector<int>> pv=get_vector(); addVector(pv); showShared_ptr(pv); return 0; }
PS:看这章好累啊,感觉比以前麻烦多了,但是还是要保持好心态,好好看,好好学,不知道这样学下去什么时候才是个尽头啊,以后怎么找工作啊