C++的学习之旅——复合类型(下)

目录

七、指针和自由空间存储

1、声明和初始化指针

2、使用new分配内存

3、使用delete释放内存

4、使用new来创建动态数组

(1)创建、释放动态数组

(2)使用动态数组

八、指针、数组和指针算术

 1、使用new创建动态结构

 2、自动存储、静态存储和动态存储

(1)自动存储

(2)静态存储

(3)动态存储

九、数组替代品

1、模板类vector

2、模板类array(C++11)

3、比较数组、vector对象和array对象


七、指针和自由空间存储

与C语言一样,可以使用&取地址,*取内容

1、声明和初始化指针

在C中,通常使用如下格式 

int *ptr;

 这里强调*ptr是一个int类型的值,而很多C++程序员使用如下格式; 

int* ptr

这里强调int*是一种指向int的指针。不过在哪里添加空格对编译器来说没有区别。

2、使用new分配内存

在C语言中,可以使用malloc来分配内存,在C++中仍然可以,不过C++提供了一种更好的方法——new运算符 

int* pn=new int;

new int告诉程序,需要一块适合存储int类型的空间,内存空间开辟后返回这块空间的地址给pn。这种只能通过指针访问

int higgens;
int* pt=&higgens;

这种则可以通过higens或指针进行访问

3、使用delete释放内存

delete用于释放使用后的内存(最初只能是由new分配的),使用格式如下:

int* ps=new int;
...
delete ps;

这将释放ps指向的内存,不过不会删除ps指针,依旧可以使用ps去指向另外新分配的内存

4、使用new来创建动态数组

以传统方式声明数组,程序在编译的时候就会给数组分配内存空间(静态联编),而使用new,则是在运行阶段,需要数组才创建它(动态联编)

(1)创建、释放动态数组

为数组分配内存和释放内存的通用格式如下:

type_name * pointer_name=new type_name[num_elements]; 
...
delete[] pointer_name;

如:
int * psome=new int[10];
...
delete[] posome

(2)使用动态数组

可以像数组一样使用动态数组

int * psome=new int [10];
psome[0]=2;//赋值
psome[1]=3;

不过使用指针和数组名之间有根本差别

#include <iostream>
#include <string>
#include <cstring>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */

int main(int argc, char** argv) {
	using namespace std;
	
	double * p3=new double [3];
	p3[0]=0.2;
	p3[1]=0.5;
	p3[2]=0.8;
	
	cout <<"P3[1] is "<<p3[1]<<endl;
	p3=p3+1;
	cout<<"Now p3[0] is "<<p3[0]<<" p3[1] is "<<p3[1]<<endl; 
	
	return 0;
}

例如这个例子,在经过p3=p3+1后,p3[0]变成p3[1]了,这是由于p3是指针变量,加1后可以指向下一个地址,不过数组名不可以这么使用

使用动态数组可以节省内存空间,另外,new和delete还可以用于字符串,同样起节省内存空间的作用

#include <iostream>
#include <cstring>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
char * getname(void);

int main(int argc, char** argv) {

	char * name;
	
//输出存储内容、地址、字节数
	name=getname();
	cout<<name<<" at "<<(int*)name<<" have :"<<strlen(name)<<"byte"<<endl;
	delete[] name;
	
	name=getname();
	cout<<name<<" at "<<(int*)name<<" have :"<<strlen(name)<<"byte"<<endl;
	delete[] name;
	return 0;
}

char * getname()
{
	char temp[80];
	cout<<"Enter last name:";
	cin>>temp;
	char * pn=new char[strlen(temp)+1];//创建比输入字节多1(保存'\0')的内存空间1
	strcpy(pn,temp);
	
	return pn;
}

八、指针、数组和指针算术

关于指针的使用、指针与数组、字符串的关系以及指针算术可以观看我前面关于C语言指针的文章,使用方法基本一致,此外书籍在这里还陈述了如何使用new创建动态结构以及自动存储、静态存储,动态存储。 

 1、使用new创建动态结构

        在运行时创建数组优于在编译时创建数组,对于结构也是如此。需要在程序运行时为结构分配所需的空间,这也可以使用 new 运算符来完成。通过使用 new,可以创建动态结构。同样,“动态”意味着内存是在运行时,而不是编译时分配的。由于类与结构非常相似,因此本节介绍的有关结构的技术也适用于类.

        将new 用于结构由两步组成:创建结构和访问其成员。要创建结构,需要同时使用结构类型和 new。例如,要创建一个未命名的 inflatable 类型,并将其地址赋给一个指针,可以这样做:

inflatable * ps = new inflatable;

        这将把足以存储 inflatable 结构的一块可用内存的地址赋给 ps。这种句法和 C的内置类型完全相同。

        比较棘手的一步是访问成员。创建动态结构时,不能将成员运算符句点用于结构名,因为这种结构没有名称,只是知道它的地址。C++专门为这种情况提供了一个运算符:箭头成员运算符(->)。该运算符由连字符和大于号组成,可用于指向结构的指针,就像点运算符可用于结构名一样。例如,如果 ps 指向一个inflatable 结构,则ps->price 是被指向的结构的 price 成员。另外,还可以使用(*ps).price访问结构成员。*ps即被指向的结构本身。

使用一个例子来应用动态结构 

#include <iostream>
#include <cstring>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;

struct inflatable 
{
	char name[20];
	float volume;
	double price;
};//创建结构体 

int main(int argc, char** argv) {
	
	inflatable * ps=new inflatable;
	cout<<"Enter name of inflatable item:";
	cin.get(ps->name,20);//cin.get()用于输入整行字符串 
	cout<<"Enter volume in cubic feet:";
	cin>>(*ps).volume;
	cout<<"Enter price:";
	cin>>ps->price;
	cout<<"name: "<<(*ps).name<<endl;
	cout<<"volume: "<<ps->volume<<" cubic feet\n";
	cout<<"price: "<<ps->price<<endl;
	
	return 0;
}

 2、自动存储、静态存储和动态存储

        根据用于分配内存的方法,C++有3 种管理数据内存的方式:自动存储、静态存储和动态存储(有时也叫作自由存储空间或堆)。在存在时间的长短方面,以这3 种方式分配的数据对象各不相同。下面简要地介绍每种类型(C++11还新增了第四种类型一一线程存储)。

(1)自动存储

        在函数内部定义的常规变量使用自动存储空间,被称为自动变量(automatic variable),这意味着它们在所属的函数被调用时自动产生,在该函数结束时消亡。
        实际上,自动变量是一个局部变量,其作用域为包含它的代码块。代码块是被包含在花括号中的一段代码。代码块不止是整个函数。函数内也可以有代码块。如果在其中的某个代码块定义了一个变量,则该变量仅在程序执行该代码块中的代码时存在。
        自动变量通常存储在栈中。这意味着执行代码块时,其中的变量将依次加入到栈中,而在离开代码块时,将按相反的顺序释放这些变量,这被称为后进先出(LIFO)。因此,在程序执行过程中,栈将不断地增大和缩小。

(2)静态存储

        静态存储是整个程序执行期间都存在的存储方式。使变量成为静态的方式有两种:一种是在函数外面定义它;另一种是在声明变量时使用关键字static:
static double fee =56.50;
        在K&RC中,只能初始化静态数组和静态结构,而C+Release2.0(及后续版本)和ANSIC中也可以初始化自动数组和自动结构。然而,一些您可能已经发现,有些 C++实现还不支持对自动数组和自动结构的初始化。

(3)动态存储

         new和delete运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池,这在C++中被称为自由存储空间(free store)或堆(heap)。该内存池同用于静态变量和自动变量的内存是分开的。new和 delete 让您能够在一个函数中分配内存,而在另一个函数中释放它。因此,数据的生命周期不完全受程序或函数的生存时间控制。与使用常规变量相比,使用new和delete让程序员对程序如何使用内存有更大的控制权。然而,内存管理也更复杂了。在栈中,自动添加和删除机制使得占用的内存总是连续的,但new和delete的相互影响可能导致占用的自由存储区不连续,这使得跟踪新分配内存的位置更困难。

九、数组替代品

1、模板类vector

        模板类 vector 类似于 string 类,也是一种动态数组。可以在运行阶段设置 vector 对象的长度,可在末尾附加新数据,还可在中间插入新数据。基本上,它是使用 new 创建动态数组的替代品。实际上,vector类确实使用new.和 delete 来管理内存,但这种工作是自动完成的。

使用vector有一些注意事项:

①要使用 vector 对象,必须包含头文件 vector

②vector 包含在名称空间 std 中,可使用using 编译指令using声明或std:vector。

③模板使用不同的语法来指出它存储的数据类型。

④vector 类使用不同的语法来指定元素数。

下面是一些示例:

#include <vector>
...
using namespace std;
vector<int> vi;
int n;
cin>>n;
vector<double> va(n);

        其中,vi是一个 vector<int>对象,vd 是一个 vector<double>对象。由于 vector 对象插入或添加值时自动调整长度,因此可以将 vi的初始长度设置为零。但要调整长度,需要使用 vector 包中的各种方法。一般而言,下面的声明创建一个名为 vt 的 vector 对象,它可存储n_elem 个类型为 typeName 的元素

vector<typeName> vt(n_elem);

 其中参数 n_elem 可以是整型常量,也可以是整型变量

2、模板类array(C++11)

        vector 类的功能比数组强大,但付出的代价是效率稍低。如果需要的是长度固定的数组,使用数组是更佳的选择,但代价是不那么方便和安全。有鉴于此,C+11新增了模板类 array,它也位于名称空间std中。与数组一样,array 对象的长度也是固定的,也使用栈(静态内存分配),而不是自由存储区,因此效率与数组相同,但更方便,更安全。要创建array对象,需要包含头文件 array。array 对象的创建语法vector 稍有不同 

#include <array>
...
using namespace std;
array<int,5> ai;
array<double,4> ad={1.1,1.2,2.3,3.4};

 推而广之,声明创建一个名为 arr 的array 对象,它包含n_elem 个类型为 typename 的元素:

array<typename,n_elem> arr;

 与创建vector对象不同,n_elem不能是变量

3、比较数组、vector对象和array对象

①无论是数组、vector 对象还是 array 对象,都可使用标准数组表示法来访问各个元素。

②array对象和数组存储在相同的内存区域(即栈)中而 vector 对象存储在另一个区域(自由存储区或堆)中。

③可以将一个amay 对象赋给另一个array对象;而对于数组,必须逐元素复制数据。 


C++的学习笔记持续更新中~

要是文章有帮助的话,就点赞收藏关注一下啦!

感谢大家的观看

欢迎大家提出问题并指正~

猜你喜欢

转载自blog.csdn.net/qq_47134270/article/details/128601048