标签:com 对象位置 而不是 创建对象 构造函数 10个 表达 src 填充
为了让 new
分配一个对象数组,要在类型名之后跟一对方括号,在其中指明要分配的对象的数目,返回指向第一个对象的指针,方括号中的大小必须是整型,但不必是常量:
int* pia = new int[get_size()]; // pia 指向第一个int
也可以使用一个表示数组类型的类型别名来分配一个数组,new
表达式中就不需要方括号了:
typedef int arrT[42]; // arrT 表示42个int的数组类型
int* p = new arrT; // 分配一个42个int的数组,p指向第一个int
虽然称 new T[]
分配的内存称为 “动态数组”,但是使用 new
分配一个数组时,但是我们并未得到一个数组类型的对象,而是得到一个数组元素类型的类型。
由于分配的内存并不是一个数组类型,因此不能对动态数组调用 begin
或 end
来返回指向首元素和尾后元素的指针。出于相同的原因,也不能使用范围 for
语句来处理动态数组中的元素。
注意:
动态数组并不是数组类型。
默认情况下,new 分配的对象,不管是单个分配的还是数组中的,都是默认初始化的。可以对数组中的元素进行值初始化,方法是在大小之后跟一对空括号:
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
在新标准中,可以提供一个原始初始化器的花括号列表:
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')};
如果初始化器数目小于元素数目,剩余元素将进行值初始化。如果初始化器大于元素数目,则 new表达式失败,不会分配任何内存。
size_t n = get_size(); //get_size 返回需要的元素数目
int *p = new int[n]; //分配数组保存元素
for(int *q = p;q != p + n;++q)
{//处理数组}
上面的代码,如果 get_size()
返回的是0 程序也能正常运行。虽然不能创建一个大小为0的静态数组对象,但是调用 new[0]
是合法的:
char arr[0]; //错误,不能定义长度为0的静态数组
char *cp = new char[10]; //正确,但是cp不能解引用
当用 new
分配一个大小为0的数组时,new
返回一个合法的非空指针。此指针保证与 new
返回的其它任何指针都不相同。对于零长度的数组来说,此指针就像尾后指针一样,可以像使用尾后迭代器一样使用这个指针。可以使用此指针进行比较操作,可以向此指针加上或者减去0,也可以从这个指针减去自从而得到0,但此指针不能解引用,因为它不指向任何元素。
为了释放动态数组,delete
在指针前加一个空方括号对:
delete p; //p必须指向一个动态分配的对象或为空
delete [] pa; //pa 必须指向一个动态分配的数组或为空
第二个语句销毁 pa
指向的数组中的元素,并释放对应的内存,数组中的元素被按逆序销毁,即最后一个元素首先被销毁,然后是倒数第二个,以此类推。
当释放一个指向数组的指针时,空方括号对是必须的:它指示编译器此指针指向一个对象数组的第一个元素,如果在 delete
一个指向数组的指针时忽略了方括号,或者是 delete
一个指向单一对象的指针时使用了方括号,其行为是未定义的。
typedef int arrT[42];
int *p = new arrT;
delete [] p; //即使使用类型别名来定义一个数组类型时,delete时[]也是必须的
标准库提供了一个可以管理 new 分配的数组的 unique_ptr 版本,为了用一个 unique_ptr 管理动态数组,我们必须在对象类型后面跟一对空的方括号:
unique_ptr<int []> up(new int[10]); //up指向一个包含10个未初始化int的数组
up.release(); //自动用delete销毁其指针
类型说明符中的方括号 (<int []>)
指出 up 指向一个 int 数组而不是一个 int
,由于 up
指向一个数组,当 up
销毁它管理的指针时,会自动调用 delete[]
。
指向数组的 unique_ptr 不能使用点和箭头成员运算符,而应该使用下标运算符访问数组中的元素:
for(size_t i = 10;i != 10;++i)
up[i] = i;
与 unique_ptr
不同,shared_ptr
不直接支持管理动态数组,如果希望使用 shared_ptr
管理一个动态数组,必须提供自己定义的删除器:
shared_ptr<int> sp(new int[10],[](int *p){delete [] p;});
sp.reset(); //使用提供的lambda释放数组
如果没有提供删除器,这段代码将是未定义的。默认情况下, shared_ptr
使用 delete
销毁它所指向的对象。如果此对象是一个动态数组,对其使用 delete
所产生的问题与释放一个动态数组指针时忘记 []
产生的问题一样。
shared_ptr
不直接支持动态数组管理这一特性会影响如何访问数组中的元素:
for(size_t i = 0;i != 0;++i)
*(sp.get() + i) = i; //使用 get 获取一个内置指针
shared_ptr
未定义下标运算符,而且智能指针类型不支持指针算术运算,因此,为了访问数组中的元素,必须用 get
获取一个内置指针,然后用它来访问数组元素。
new
有一些灵活性上的局限,其中一方面表现在它将内存分配和对象构造组合在一起,类似的,delete
将对象析构和内存释放组合在一起。一般当分配单个对象时,通常希望将内存分配和对象初始化组合在一起。
当分配一大块内存时,并且在这块内存上按需构造对象。在此情况下,希望将内存分配和对象构造分离,这意味着可以分配大块内存,但只在真正需要时才真正执行对象创建操作。
一般情况下,将内存分配和对象构造组合在一起可能会导致不必要的浪费:
string *const p = new string[n]; //构造n个空string
string s;
string *q = p;
while(cin >> s && q != p + n)
*q++ = s;
const size_t size = q - p;
delete [] p ;
new
表达式分配并初始化了 n
个 string
,但是实际使用时可能不需要 n
个 string
,少量的 string
就足够了,这样,我们就创建了一些可能永远也用不到的对象。而且,对于那些确实要使用的对象,在初始化之后会立即赋予新值,每个使用的 元素都被赋值两次。
更重要的是,那些没有默认构造函数的类就不能动态分配数组。
allocator
定义在头文件 memory
中,它帮助我们将内存分配和对象构造分离,它提供了一种类型感知的内存分配方法,它分配的内存是原始的、未构造的。
allocate 是一个模板,为了定义一个 allocator 对象,必须指明这个 allocator 可以分配的对象类型。当一个 allocator 对象分配内存时,它会根据给定对象类型来确定恰当的内存大小和对齐位置:
allocator<string> alloc;
auto const p = alloc.allocate(n); //分配n个未初始化的 string
allocator
分配的内存是未构造的,后期可以在此内存中按需构造对象。construct
成员函数接受一个指针和零个或多个额外参数,在给定位置构造一个元素,额外参数用来初始化构造的对象,这些额外参数必须是与构造的对象的类型相匹配的合法的初始化器:
auto q = p;
alloc.construct(q++); //*为空字符串
alloc.construct(q++,10,'c'); //*q为cccccccccc
alloc.construct(q++,"hi"); //*q为 hi
construct
只接受两个参数:指向创建对象位置的指针和一个元素类型的值。
还未构造对象的情况下就使用原始内存是错误的:
cout<< *p <<endl; //正确,使用 string 的输出运算符
cout<< *q <<endl; //灾难,q指向未构造的内存
注意:
为了使用 allocate
返回的内存,必须使用 construct
构造对象,使用未构造的内存,其行为是未定义的。
当使用完对象后,必须对每个构造的元素调用 destroy
来销毁它们,函数 destroy
接受一个指针,对指向的对象执行析构函数:
while (q != p)
alloc.destroy(--q);
在循环开始处,q
指向最后构造的元素之后的位置。在调用 destroy
之前对 q
进行了递减操作。
释放内存通过调用 deallocate
来完成:
alloc.deallocate(p,n);
传递给 deallocate
的指针不能为空,它必须指向由 deallocate
分配的内存,且传递给 deallocate
的大小参数必须与调用 allocate
分配内存时提供的大小参数值相等。
allocator
类定义了两个伴随算法,可以在未初始化内存中创建对象,它们也定义在头文件 memory
中。
auto p = alloc.allocate(vi.size() *2); //分配比vi中元素所占用空间大一倍的动态内存
auto q = uninitialized_copy(vi.begin(),vi.end(),p); //拷贝vi中的元素构造从p开始的元素
uninitialized_fill_n(q,vi.size(),42); //将剩余的元素初始化为42
uninitialized_copy
调用会返回一个指针,指向最后一个构造的元素之后的位置。
标签:com 对象位置 而不是 创建对象 构造函数 10个 表达 src 填充
原文地址:https://www.cnblogs.com/xiaojianliu/p/12496821.html