C++ Primer 第十二章 12.2 动态数组 练习和总结

12.2动态数组

之前学到new和delete是可以创建/删除一个对象的,但是我们有时候需要创建一组对象和删除一组对象。现在我们可以使用容器来这么做,vector,list,不过new和delete也提供了创建/删除一组对象的操作。

创建动态数组

使用new创建动态数组

我们可以使用如下的方式用new创建动态数组。

int* p = new int[10];
using TenArr = int[10];
int* p = new TenArr;

需要注意的是,动态创建的数组返回的是首个元素的指针,而不是数组类型,因此p不能使用范围for和begin( p ),end( p ).

如果在创建动态数组的时候,没有显式的初始化,则使用默认初始化。
我们可以使用()显式的初始化,也可以使用初始化列表进行初始化。但是不可以在()中写初始化列表。

int *p = new int[10];//默认初始化
int *p = new int[10]();//显式的初始化
int *p = new int[10]{1,2,3,4,45,56};//列表初始化
int *p - new int[10]({1});//错误

我们可以创建一个大小为0的动态数组。

int a[0];//error
int *a = new int[0];//正确

使用new创建的大小为0的数组,返回的指针不指向任何对象,相当于一个尾后迭代器。

智能指针和动态数组

C++标准库提供了可以管理new分配的数组的unique_ptr版本。

在创建这个版本的unique_ptr,需要在尖括号内写上[]

unique_ptr<int[]> p(new int[10]);

因为p保存的是一个数组,所以我们不能使用成员访问符来访问其中的元素。即“.”和"->",但是我们可以使用下标运算符来访问。p[1]=xxx;

当删除该智能指针时,p调用delete [] 来回收内存。

管理动态数组的unique_ptr操作。
在这里插入图片描述

C++标准库没有提供来管理new的动态数组的shared_ptr版本。

但是我们还是可以使用,不过要自己提供一个代替delete[]的函数,由于C++标准库本身没有管理动态数组类型的shared_ptr,所以我们不能使用下标运算符来访问其中的元素,而是只能使用p+n的方式来访问元素。

删除动态数组

对于使用普通指针来管理的动态数组类型,我们使用delete [] ptr;来删除这个动态数组。

注意,delete []ptr要求ptr必须是动态数组,或者nullptr。
而delete ptr。要求ptr,必须是new出来的一个对象,或者nullptr。

如果对动态数组使用delete ptr。或者对一个对象使用delete []ptr这都是不符合预期的,这样的操作可能会让程序在运行期间发生错误。

智能指针管理的动态数组当引用计数为0时,为维护的指针调用delete []

练习

12.23

在拷贝的时候可以考虑使用strcpy,但是vs2017不推荐使用这个函数,编译会报错

记住C风格字符串是以’\0’结尾的

void combine_two_str_v1(const char* str1,const char* str2) {
	size_t len = 0;
	size_t str1_len = 0;
	const char* temp = str1;
	while (*temp!='\0') {
		++temp;
		++len;
	}
	temp = str2;
	str1_len = len;
	while (*temp!='\0') {
		++temp;
		++len;
	}
	char* char_arr = new char[len + 1];
	char* temp_char_arr = char_arr;
	temp = str1;
	while (*temp!='\0') {
		*temp_char_arr = *temp;
		++temp;
		++temp_char_arr;
	}
	temp = str2;
	while (*temp!='\0') {
		*temp_char_arr = *temp;
		++temp;
		++temp_char_arr;
	}
	//在结尾处还需要加上\0
	*temp_char_arr = '\0';
	cout << char_arr << endl;
	delete [] char_arr;
}
void combine_str_v2(const string& str1,const string& str2) {
	char* char_arr = new char[str1.size() + str2.size() + 1];
	char* temp_char_arr = char_arr;
	for (auto const &item:str1) {
		*temp_char_arr = item;
		++temp_char_arr;
	}
	for (auto const& item:str2) {
		*temp_char_arr = item;
		++temp_char_arr;
	}
	*temp_char_arr = '\0';
	cout << char_arr << endl;
	delete[]char_arr;
}
12.24

使用一个足够大的数组来存入字符串,在VS2017中,输入的字符超过了数组所能存储的大小也没有报错,但是这可能造成了未知的隐患。

char* char_arr = new char[10];
	while (cin>>char_arr) {
		cout<<char_arr<<endl;
	}
	delete[]char_arr;
12.25
delete []pa;

12.2.2 allocator类

使用new创建的对象,分配空间和初始化对象是绑定在一起的。然而我们有时会在初始化之后为对象再一次的赋值,这就让第一次的初始化没有任何意义。

另一种情况是,我们创建了一个动态数组,但是使用时并没有使用那么多对象,而是使用了部分对象,这也让没有使用的对象的初始化没有意义。

还有一种情况是,某一些类对象没有默认构造函数,那么我们在new一个类的时候,就必须提供初值。

为了更加的灵活的分配内存空间和初始化对象,C++提供了allocator类型,它是一个泛型类型,其将分配空间和初始化对象分开了。也将销毁对象和回收内存给分开了。

allocator类型定义在memory头文件中。

其操作如下,四个函数分别时,分配空间,回收空间,初始化对象,销毁对象。
在这里插入图片描述
缺点是,我们初始化和销毁对象只能一个一个的初始化和销毁。

所以标准库为allocator提供了两个伴随算法,copy和fill,用于批量的初始化对象。
在这里插入图片描述

在具体使用allocator时,我们的步骤是
1.开辟空间
2.初始化对象
3.销毁对象
4.回收空间

下面的练习就是一个很好的例子。

练习

可以看到这种把分配内存和初始化分开的方法,非常的繁琐,但是胜在更加灵活

//创建一个对象,用于分配内存
	std::allocator<int> a;
	//分配内存
	size_t total_size = 10;
	auto p = a.allocate(total_size);
	//为前5个初始化
	size_t  init_size = 5;
	//批量的初始化对象
	std::uninitialized_fill_n(p,init_size,10);
	//auto q = p;
	//使用分配的空间
	for (size_t i = 0; i < init_size;++i) {
		cout << *(p + i) << endl;
	}
	//一个一个的销毁对象
	for (size_t i = 0; i < init_size;++i) {
		a.destroy(p+i);
	}
	//回收空间
	a.deallocate(p,total_size);
发布了54 篇原创文章 · 获赞 6 · 访问量 3307

猜你喜欢

转载自blog.csdn.net/zengqi12138/article/details/104412449