首页 > 代码库 > 【足迹C++primer】40、动态数组

【足迹C++primer】40、动态数组

动态数组
C++语言定义了另外一种new表达式语法,可以分配并初始化一个对象数组。标准库中包含
一个名为allocator的类,允许我们将分配和初始化分离。

12.2.1 new和数组

void fun1()
{
   int *pia=new int[2]; //pia指向第一个int
   //方括号中的大小必须是整型,但不必是常量
   typedef int arrT[42];    //arrT表示42个int的数组类型
   int *p=new arrT;         //分配一个42个int的数组;p指向第一个int
   //实际上,编译的时候还是这样的
   int *p1=new int[42];
}

分配一个数组会得到一个元素类型的指针

由于分配的内存并不是一个数组类型,因此不能对动态数组调用begin或end
也不能使用范围for语句来处理动态数组中的元素

初始化动态分配对象的数组

    int *pia=new int[10];           //10个未初始化的int
    int *pia2=new int[10]();        //10个值初始化为0的int
    string *psa=new string[10];     //10个空string
    string *psa2=new string[10]();  //10个空string
    //新标准中,我们还可以这样写
    //10个int分别用列表中对应的值进行初始化
    int *pia3=new int[10]{0,1,2,3,4,5,6,7,8,9};
    //10个string类型的前面4个用给的值进行初始化,后面的进行值初始化
    string *psa3=new string[10]{"a","an","the",string(3,'x')};

动态分配一个空数组是合法的

    size_t n=10,n2=0;
    int *p=new int[n];  //分配数组保存元素
    for(int* q=p ; q != p ; ++q)
    {
        /* 处理数组 */
    }

若n为0,new会分配0个对象。for循环中的条件会失败(p等于q+n,因为n为0)。
因此,循环体不会被执行

释放动态数组

    int *p=new int;
    int *pa=new int[110];
     //为了释放动态数组,我们使用一种特殊形式的delete——在指针前加上一方括号对
     delete p;  //p必须指向一个动态分配的对象或为空
     delete [] pa;  //pa必须指向一个动态分配的数组或为空

     typedef int arrT[42];      //arrT是42个int的数组的类型别名
     int *p1=new arrT;           //分配一个42个int的数组;p指向第一个元素
     delete [] p1;               //方括号是必须的,因为我们当初分配的是一个动态数组

不管外面如何,p指向的是一个对象数组的首元素,而不是一个类型为arrT的单一对象。
因此释放的时候必须加上方括号

智能指针和动态数组

    //up指向一个包含10个未初始化的int的数组
    unique_ptr<int[]> up(new int[10]);
    up.release();       //自动用delete[]销毁其指针
    //当一个unique_ptr指向一个动态数组的时候我们可以用下标访问数组中的元素
    for(size_t i=0 ; i != 10 ; ++i)
        up[i]=i;        //为每个元素赋予一个新值

    和unique_ptr不同,shared_ptr不直接支持管理动态数组。如果希望shared_ptr管理一个动态
    数组,那么就必须自己提供自己定义的删除器
    //为了适应shared_ptr,必须提供自己定义的删除器
    shared_ptr<int> sp(new int[10], [](int *p){delete [] p;});
    sp.reset(); //使用我们提供的lambda释放数组,它使用delete[]
    //shared_ptr未定义下标运算,也不支持下标运算
    for(size_t i=0 ; i != 10 ; ++i)
        *(sp.get()+i)=i;        //使用get获取一个内置指针

12.23

/**
12.23
编写一个程序,链接两个字符串字面常量,将结果保存在一个动态分配的char数组中
重写这个程序,连接两个标准库string对象
*/
void fun6()
{
    string s1="cutter",s2="_point";
    unique_ptr<char[]> upc(new char[20]);

    for(size_t i=0 ; i != s1.size()+s2.size() ; ++i)
    {
        if(i < s1.size())
            upc[i]=s1[i];
        else
            upc[i]=s2[i-s1.size()];
    }

    for(int j=0 ; j != 20 ; ++j)
        cout<<upc[j];


    upc.release();      //自动用delete[]销毁其指针

}

12.2.2allocator类

一般情况下,将内存分配和对象构造结合在一起可能会导致不必要的浪费
    int n;
    string *const p=new string[n];      //构造n个空string
    string s;
    string *q=p;        //q指向第一个string
    while(cin>>s && q != p+n)
        *q++=s;         //赋予*q一个新值
    const size_t size=q-p;      //记住我们读取了多少个string
    //使用数组
    delete [] p;        //p指向一个数组;记得用delete[]来释放

但是,我们可能不需要n个string,少量string可能就足够了。

allocator类

    int n;
    allocator<string> alloc;        //可分配string的allocator对象
    auto const p=alloc.allocate(n); //分配n各未初始化的string
    /*
    allocator分配微构造的内存
    */
    auto q=p;     //这里q指向和p一样
    alloc.construct(q++);   //*q为空字符串
    alloc.construct(q++, 10, 'c');  //*q为cccccccccc
    alloc.construct(q++, "hi");     //*q为hi!
    //q指向最后构造的元素之后的位置
    cout<<*p<<endl; //正确;使用string输出运算符
//    cout<<*q<<endl; //灾难:q指向微构造的内存

    while(q != p)
        alloc.destroy(--q);     //释放我们真正构造的string

全部代码展示

/**
* 功能:动态数组
* 时间:2014年7月9日10:05:01
* 作者:cutter_point
*/

#include<iostream>
#include<memory>
#include<string>
#include<vector>

using namespace std;

/**
C++语言定义了另外一种new表达式语法,可以分配并初始化一个对象数组。标准库中包含
一个名为allocator的类,允许我们将分配和初始化分离。
*/

/**
12.2.1 new和数组
*/
void fun1()
{
   int *pia=new int[2]; //pia指向第一个int
   //方括号中的大小必须是整型,但不必是常量
   typedef int arrT[42];    //arrT表示42个int的数组类型
   int *p=new arrT;         //分配一个42个int的数组;p指向第一个int
   //实际上,编译的时候还是这样的
   int *p1=new int[42];
}

/**
分配一个数组会得到一个元素类型的指针
*/

/*
由于分配的内存并不是一个数组类型,因此不能对动态数组调用begin或end
也不能使用范围for语句来处理动态数组中的元素
*/


/**
初始化动态分配对象的数组
*/
void fun2()
{
    int *pia=new int[10];           //10个未初始化的int
    int *pia2=new int[10]();        //10个值初始化为0的int
    string *psa=new string[10];     //10个空string
    string *psa2=new string[10]();  //10个空string
    //新标准中,我们还可以这样写
    //10个int分别用列表中对应的值进行初始化
    int *pia3=new int[10]{0,1,2,3,4,5,6,7,8,9};
    //10个string类型的前面4个用给的值进行初始化,后面的进行值初始化
    string *psa3=new string[10]{"a","an","the",string(3,'x')};
}

/**
动态分配一个空数组是合法的
*/
void fun3()
{
    size_t n=10,n2=0;
    int *p=new int[n];  //分配数组保存元素
    for(int* q=p ; q != p ; ++q)
    {
        /* 处理数组 */
    }

   /*
    若n为0,new会分配0个对象。for循环中的条件会失败(p等于q+n,因为n为0)。
    因此,循环体不会被执行
    */
}

/**
释放动态数组
*/
void fun4()
{
    int *p=new int;
    int *pa=new int[110];
     //为了释放动态数组,我们使用一种特殊形式的delete——在指针前加上一方括号对
     delete p;  //p必须指向一个动态分配的对象或为空
     delete [] pa;  //pa必须指向一个动态分配的数组或为空

     typedef int arrT[42];      //arrT是42个int的数组的类型别名
     int *p1=new arrT;           //分配一个42个int的数组;p指向第一个元素
     delete [] p1;               //方括号是必须的,因为我们当初分配的是一个动态数组

     /*
        不管外面如何,p指向的是一个对象数组的首元素,而不是一个类型为arrT的单一对象。
        因此释放的时候必须加上方括号
    */
}

/**
智能指针和动态数组
*/
void fun5()
{
    //up指向一个包含10个未初始化的int的数组
    unique_ptr<int[]> up(new int[10]);
    up.release();       //自动用delete[]销毁其指针
    //当一个unique_ptr指向一个动态数组的时候我们可以用下标访问数组中的元素
    for(size_t i=0 ; i != 10 ; ++i)
        up[i]=i;        //为每个元素赋予一个新值
    /*
    和unique_ptr不同,shared_ptr不直接支持管理动态数组。如果希望shared_ptr管理一个动态
    数组,那么就必须自己提供自己定义的删除器
    */
    //为了适应shared_ptr,必须提供自己定义的删除器
    shared_ptr<int> sp(new int[10], [](int *p){delete [] p;});
    sp.reset(); //使用我们提供的lambda释放数组,它使用delete[]
    //shared_ptr未定义下标运算,也不支持下标运算
    for(size_t i=0 ; i != 10 ; ++i)
        *(sp.get()+i)=i;        //使用get获取一个内置指针
}

/**
12.23
编写一个程序,链接两个字符串字面常量,将结果保存在一个动态分配的char数组中
重写这个程序,连接两个标准库string对象
*/
void fun6()
{
    string s1="cutter",s2="_point";
    unique_ptr<char[]> upc(new char[20]);

    for(size_t i=0 ; i != s1.size()+s2.size() ; ++i)
    {
        if(i < s1.size())
            upc[i]=s1[i];
        else
            upc[i]=s2[i-s1.size()];
    }

    for(int j=0 ; j != 20 ; ++j)
        cout<<upc[j];


    upc.release();      //自动用delete[]销毁其指针

}


/**
allocator类
*/
/*
一般情况下,将内存分配和对象构造结合在一起可能会导致不必要的浪费
*/
void fun7()
{
    int n;
    string *const p=new string[n];      //构造n个空string
    string s;
    string *q=p;        //q指向第一个string
    while(cin>>s && q != p+n)
        *q++=s;         //赋予*q一个新值
    const size_t size=q-p;      //记住我们读取了多少个string
    //使用数组
    delete [] p;        //p指向一个数组;记得用delete[]来释放
    /*
    但是,我们可能不需要n个string,少量string可能就足够了。
    */
}

/*
allocator类
*/
void fun8()
{
    int n;
    allocator<string> alloc;        //可分配string的allocator对象
    auto const p=alloc.allocate(n); //分配n各未初始化的string
    /*
    allocator分配微构造的内存
    */
    auto q=p;     //这里q指向和p一样
    alloc.construct(q++);   //*q为空字符串
    alloc.construct(q++, 10, 'c');  //*q为cccccccccc
    alloc.construct(q++, "hi");     //*q为hi!
    //q指向最后构造的元素之后的位置
    cout<<*p<<endl; //正确;使用string输出运算符
//    cout<<*q<<endl; //灾难:q指向微构造的内存

    while(q != p)
        alloc.destroy(--q);     //释放我们真正构造的string
}

int main()
{
    fun6();
    return 0;
}


PS:花了一早上,就搞了这么点东西,虽然中途休息了一会,并且还玩了半个多小时的游戏大笑,但是哥还是把这玩意全部搞完了,哈哈搞完了就可以好好玩一玩啦!!