目录
七、指针和自由空间存储
与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++的学习笔记持续更新中~
要是文章有帮助的话,就点赞收藏关注一下啦!
感谢大家的观看
欢迎大家提出问题并指正~