用new和delete运算符进行动态分配和撤销存储空间

  • C++内存分配有两种方式:静态分配和动态分配
  • 静态分配指在编译时为程序中的数据对象分配相应的存储空间。由于是在编译时为数据对象分配存储空间,因此就要求在编译时空间大小必须是明确的,所以数组的长度必须是常量。而一旦编译完成,运行期间这个数组的长度就是固定不变的。
  • 动态分配是程序运行期间根据实际需要动态地申请或释放内存的方式,它不像数组等静态内存分配方式那样需要预先分配存储空间,而是根据程序的需要适时分配,且分配的大小就是程序要求的大小。
  • 因此,动态分配方式有如下特点:
    ①不需要预先分配存储空间
    ②分配的空间可以根据程序的需要扩大或缩小
  • 动态分配的缺点是运行效率不如静态分配,因为它的分配和释放会产生额外的调用开销。实际编程中,在运行时分配或内存大小需要随时调整等情况下才使用动态分
    配方式。

用new和delete运算符进行动态分配和撤销存储空间

  • 在软件开发中,常常需要动态地分配和撤销空间,例如对动态链表中结点的插入与删除。
  • 在C语言中是利用库函数malloc和free来分配和撤销内存空间的。 但是使用malloc函数时必须制定需要开辟的内存空间大小,其调用形式为void * malloc(size_t size);。size是字节数,需要人们事先求出或用sizeof运算符由系统求出。
    例:int * p = (int * ) malloc(sizeof(int));
    此外,malloc函数只能从用户处知道应开辟空间的大小而不知道数据的类型,因此无法使其返回的指针指向具体的数据。其返回值一律为 void * 类型,必须在程序中进行强制转化,才能使其返回的指针指向具体的数据。
  • C++提供了较简便而功能较强的运算符new和delete来取代malloc和free函数。
  • 注意: new和delete是运算符,不是函数,因此执行效率高。虽然为了与C语言兼容,C++仍保留malloc和free函数,但建议用户不用malloc和free函数,而用new和delete运算符

new运算符使用的一般格式为:new 类型 [初值]
  • 注意:用new分配数组空间时不能指定初值。如果由于内存不足等原因而无法正常分配空间,则new会返回一个空指针NULL,用户可以根据该指针的值判断分配空间是否成功

delete运算符使用的一般格式为:delete 指针变量 (对变量) 或 delete [ ] 指针变量 (对数组)

例:

int* p1, * p2;
char* pz1;
p1 = new int; //分配一个整型空间,若成功则p1指向该空间,否则p1为NULL
p2 = new int(10); //分配一个整型空间,且给这个整型赋初值10,即*p2为10
pz1 = new char[80]; //分配一个字符数组(字符串)空间,即pz1为字符串指针
int(*pz2)[10] = new int[2][10]; //分配一个二维字符数组(字符串数组)空间,即pz2为字符串数组指针
delete p1; //释放p1指向的整型空间
delete[] pz1; //释放pz1指向的字符串空间
  1. new运算结果是指向分配得到的内存空间的指针,如果没有足够的内存空间可以分配,其运算结果是一个0值指针。
  2. 销毁对象后,指针p1变成没有定义,然而它仍然存放先前所指向的对象(已销毁)的地址,因此指针p1不再有效,这样的指针成为迷途指针。通常在delete运算之后将指针重设为0值指针,避免迷途指针。
  3. 用new创建的动态对象使用完之后,必须用delete销毁它。
  4. delete只能删除由new创建的动态对象,否则将导致程序错误

例:

#include
using namespace std;
int* f1(int n)//分配n个整形内存,返回首地址
{
	int* p, i;
	p = new int[n]; //分配
	for (i = 0; i < n; i++) p[i] = i; //赋初始值
	return p; //动态分配的指针返回是有意义的 
}

void f2(int* p, int n) //输出动态内存中的n个数据
{
	while (n-- > 0) cout << *p++ << " ";
}

void f3(int* p) 
{
	delete[] p; //释放内存 
}

int main()
{
	int* pi;
	pi = f1(5); //分配
	f2(pi, 5); //输出 
	f3(pi); //释放 
	return 0;
}

动态分配 VS 静态分配

  1. 静态内存管理由编译器进行,程序员只做对象定义(相当于分配),而动态内存管理按程序员认为的指令进行
  2. 动态内存分配和释放必须对应(malloc-free / new-delete),即有分配就必须有释放,不释放内存会产生"内存泄漏",后果是随着程序运行多次,可以使用的内存空间越来越少;另一方面,再次释放已经释放的内存空间,会导致程序出现崩溃性错误
  3. 静态分配内存的生命期由编译器自动决定,要么是程序运行期,要么是函数执行期。动态分配内存的生命期由程序员决定,即从分配时开始,至释放时结束。特别地,动态分配内存的生命期允许跨多个函数
  4. 静态分配内存的对象有初始化,动态分配内存一般需要人为的指令赋初始值
  5. 避免释放内存后出现"迷途指针",应及时设置为空指针

动态分配数组和字符串

动态分配数组

动态分配一维或多维数组的方法是由指针管理数组,二维以上数组按一维数组方式来处理,具体步骤为:
①定义指针p
②分配数组空间,用来存储数组元素,空间大小按元素个数计算
③按一维数组方式使用这个数组(例如输入、输出等)
若是一维数组,则元素为p[i];若是二维数组,则元素为p[i*M+j],其中M为列元素个数,以此类推
④释放数组空间

例:

#include
using namespace std;
double AVE(double* A, int N) //计算N*N二维数组元素的平均值
{ 
	int sum = 0;
	for (int i = 0; i < N; i++)
		for (int j = 0; j < N; j++)
			sum = sum + *(A + i * N + j);
	return sum / (N * N);
}

int main()
{
	int n;
	cin >> n;
	double* A = new double[n * n]; //分配数组A[n][n]
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			cin >> *(A + i * n + j); //输入数据到A[i][j]
	cout << "det A=" << AVE(A, n) << endl;
	delete[]A; //释放数组
	return 0;
}

动态分配字符串

实际编程中,字符串类型表示文字信息数据,其特点是字符长度不固定。通过动态分配字符串,根据程序的需要确定字符串的实际长度。

动态分配字符串的方法是由字符指针管理字符串,具体步骤为:

  1. 定义字符指针
  2. 分配字符串空间,用来存储字符串
  3. 使用这个字符串(例如输入输出等)
  4. 释放字符串空间

例:

char *p = new char[1000]; //分配字符串空间
cin >> p; //输入字符串
cout << p; //输出字符串
delete [] p; //释放字符串空间

宏定义

  • 在C++源程序中允许用一个标识符来代表一个字符文本,称为宏,标识符为宏名。
  • 宏是由宏定义命令事先定义的。预处理时,对程序中所有后续的宏名实例(称为宏引用),预处理器都用字符文本去替换,称为宏替换或宏展开。
  • 宏定义通常用于定义程序中的符号常量、类型别名、运算式代换、语句代换等,其命令为#define,分为不带参数的宏定义和带参数的宏定义。

不带参数的宏定义

  • 不带参数的宏定义的命名形式为:#define 宏名 字符文本

例:

#define PI 3.1415926
L = 2 * PI * r;
  • 宏定义只是简单置换,不作语法检查,因此,宏串中的字符都是有效字符

带参数的宏定义

  • 带参数的宏定义的命名形式为:#define 宏名(参数表) 字符文本
  • 带参数的宏的引用形式为:宏名(引用参数)

例:

#define max(a,b) (((a)>(b))?(a):(b))
L = max(x - y, x + y);
  • 为了保证宏展开的结果符合设计本意,应在宏串或实参字串中加入必要的括号

#和##预处理运算

  • #运算符的作用是文本参数"字符串化",即出现在宏定义字符文本中的#把跟在后面的参数转换成一个C++字符串常量

例:

#define PRINT_MSG1(x) printf(#x);
#define PRINT_MSG2(x) printf(x);
PRINT_MSG1(Hello World); //正确
PRINT_MSG1("Hello World"); //正确
PRINT_MSG2(Hello World); //错误
PRINT_MSG2("Hello World"); //正确
  • ##运算符的作用是将两个字符文本连接成一个字符文本,如果其中一个字符串文本是宏定义的参数,连接会在参数替换后发生

例:

#define SET1(arg) A##arg=arg;
#define SET2(arg) Aarg=arg;
SET1(1); //宏替换为A1=1 ;
SET2(1); //宏替换为Aarg=1;

你可能感兴趣的:(C++程序设计,C++,new和delete运算符,静态分配/动态分配,宏定义)