【足迹C++primer】39、动态内存与智能指针(2)

动态内存与智能指针(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动态对象可以隐式初始化,

而其他类型的对象就必须显示初始化,由于分配的对象是const的,new返回

的指针是一个指向const的指针

内存耗尽

一旦一个程序用光了它所有可用的内存,new表达式就会失败。默认情况下,如果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赋予指针

这样就清楚地指出指针不指向任何对象

。。。这只是提供了有限的保护

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书写
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:看这章好累啊,感觉比以前麻烦多了,但是还是要保持好心态,好好看,好好学,不知道这样学下去什么时候才是个尽头啊,以后怎么找工作啊

【足迹C++primer】39、动态内存与智能指针(2)

时间: 2024-10-21 15:13:08

【足迹C++primer】39、动态内存与智能指针(2)的相关文章

【足迹C++primer】39、动态内存与智能指针(3)

动态内存与智能指针(3) /** * 功能:动态内存与智能指针 * 时间:2014年7月8日15:33:58 * 作者:cutter_point */ #include<iostream> #include<vector> #include<memory> #include<string> using namespace std; /** 智能指针和异常 */ void f() { shared_ptr<int> sp(new int(42));

C++ Primer笔记8_动态内存_智能指针

1.动态内存 C++中,动态内存管理是通过一对运算符完成的:new和delete.C语言中通过malloc与free函数来实现先动态内存的分配与释放.C++中new与delete的实现其实会调用malloc与free. new分配: 分配变量空间: int *a = new int; // 不初始化 int *b = new int(10); //初始化为10 string *str = new string(10, ); 分配数组空间: int *arr = new int[10];//分配的

C++primer第十二章读书笔记---动态内存与智能指针

    目前为止我们使用过的静态内存,栈内存和内存池,静态内存用来保存局部static对象.类static成员,以及定义在任何函数之外的成员.栈内存用来保存定义在函数内部的非static成员,分配在静态 内存或栈内存中的对象由编译器自动创建或销毁,对于栈对象仅在其定义的程序块运行时才有效,static对象在程序运行之前分配,程序结束时销毁.除了静态内存和栈内存外,每个程序还拥有一个内存池(堆)在堆上分配动态对象,当动态对象不再使用时,我们必须显示的销毁它.     (一).动态内存与智能指针  

动态内存1(动态内存与智能指针)

静态内存用来保存局部 static 对象.类 static 数据成员 以及任何定义在函数之外的变量.栈内存用来存储定义在函数内部的非 static 对象.分配在静态或栈内存中的对象由编译器自动创建和销毁.对于栈对象,仅在其定义的程序块运行时才存在:static 对象在使用之前分配,在程序结束时销毁. 除了静态内存和栈内存,每个程序还拥有一个内存池.这部分内存被称作自由空间或堆.程序用堆来存储动态分配的对象--即,那些在程序运行时分配的对象.动态对象的生存周期由程序来控制,即当动态对象不再使用时,

动态内存——动态内存与智能指针

全局对象在程序启动时分配,在程序结束时销毁.对于局部自动对象,当我们进入其定义所在的程序块时被创建,在离开块时销毁.局部static对象在第一次使用前分配,在程序结束时销毁. 除了自动和static对象外,C++还支持动态分配对象.动态分配的对象的生命期与它们在哪里创建是无关的,只有当显示地被释放时,这些对象才会销毁. 静态内存用来保存局部static对象.类static数据成员以及定义在任何函数之外的变量.栈内存用来保存定义在函数内的非static对象.分配在静态或栈内存中的对象由编译器自动创

C++笔记(12):动态内存和智能指针

动态内存和智能指针 动态内存: 1.针对堆里面存放的对象 2.使用new delete运算符 3.智能指针:shared_ptr(多个指针指向同一个对象);  unique_ptr(一个指针指向一个对象);     weak_ptr(弱引用,管理shared_ptr) 4.标准库函数:make_shared<int>()

第十二章 动态内存与智能指针

动态内存与智能指针 [智能指针]头文件#include<memory>shared_ptr: 允许多个指针指向同一个对象unique_ptr: "独占"所指向的对象weak_ptr:伴随类,它是一种弱引用,指向shared_ptr所管理的对象. 原文地址:https://www.cnblogs.com/sunbines/p/8552298.html

动态内存和智能指针

c++中动态内存的管理是通过一对运算符来完成的:new.在动态内存中为对象分配空间并返回一个指向该对象的指针,我们呢,可以选择对对象进行初始化,delete接受一个动态对象的指针,销毁该对象,并返回与之关联的内存. 动态内存的使用的不足: 1.保证在正que的时间释放内存是及其困难 2.有时我们会忘记释放内存,这样就导致内存泄漏 3.有时还存在引用内存的情况,但是我们已经释放了它,这样就导致非法内存的指针 这样就产生了智能指针(smart pointer)来动态管理内存,智能指针优点: 1.行为

12.1 动态内存与智能指针

//练习12.10-11 shared_ptr<int> p(new int(42)); //process(p); //process(shared_ptr<int>(p)); //用p来初始化创建一个临时的智能指针 //也指向p所指向的内存 process(shared_ptr<int>(p.get())); //用p.get()返回的内置指针来初始化创建一个 //临时的智能指针,指向p所指向的内存 //当离开process函数的作用域时,这个临时智能指针会被销毁