【ONE·C++||内存管理和模板初阶】-创新互联

总言

  学习笔记,慢慢补充。

创新互联主要从事网站设计、网站制作、网页设计、企业做网站、公司建网站等业务。立足成都服务盂县,10余年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:18980820575文章目录
  • 总言
  • 1、C/C++内存分布
    • 1.1、内存区域划分简介
  • 2、动态内存分布管理
    • 2.1、C语言中动态内存管理:malloc、calloc、rellaooc、free
    • 2.2、C++中动态内存管理 :new、delete
      • 2.2.1、针对内置类型
      • 2.2.2、针对自定义类型
      • 2.2.3、new申请动态内存失败后如何处理
    • 2.2、operator new与operator delete全局函数
      • 2.2.1、关于new、delete功能的底层原理
      • 2.2.2、重载operator new与operator delete
      • 2.2.3、new、delete实现原理总结
    • 2.3、定位new
      • 2.3.1、是什么和为什么
  • 3、模板初阶
    • 3.1、泛型编程
    • 3.2、函数模板
      • 3.2.1、函数模板的概念和基本使用方式
      • 3.2.2、函数模板底层原理
      • 3.2.3、函数模板的实例化:隐式、显式等
      • 3.2.4、模板参数的匹配原则
    • 3.3、类模板
      • 3.3.1、为什么和是什么
      • 3.3.2、模板实例化说明
      • 3.3.3、以Stack的实现演示模板使用

  

1、C/C++内存分布 1.1、内存区域划分简介

  1)、问题引入:

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{static int staticVar = 1;
	int localVar = 1;
	int num1[10] = {1, 2, 3, 4 };
	char char2[] = "abcd";
	const char* pChar3 = "abcd";
	int* ptr1 = (int*)malloc(sizeof(int) * 4);
	int* ptr2 = (int*)calloc(4, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
	free(ptr1);
	free(ptr3);
}

在这里插入图片描述选择题:
  选项: A、栈  B、堆   C、数据段(静态区)   D、代码段(常量区)

  1、globalVar在哪里?____   staticGlobalVar在哪里?____
  2、staticVar在哪里?____   localVar在哪里?____
  3、num1在哪里?____
  4、char2在哪里?____   *char2在哪里?___
  5、pChar3在哪里?____   *pChar3在哪里?____
  6、ptr1在哪里?____   *ptr1在哪里?____
在这里插入图片描述

在这里插入图片描述填空题:

  1、填空题:
  sizeof(num1)= ____  
  sizeof(char2)= ____  strlen(char2)= ____  
  sizeof(pChar3)= ____  strlen(pChar3)= ____  
  sizeof(ptr1)= ____  
  3、 sizeof 和 strlen 区别?
  
  
  
  2)、划分区域示意图:
在这里插入图片描述

  
  
  

2、动态内存分布管理 2.1、C语言中动态内存管理:malloc、calloc、rellaooc、free

  此处详细讲解请看C动态内存章。
  
  

2.2、C++中动态内存管理 :new、delete

  1)、C++可以兼容C,为什么还需要提出自己的内存管理方式?
   1、首先要明确的是,C++兼容C,故malloc、calloc、rellaooc、free这些在C++中照样可以用。
   2、但有些地方,C中的动态内存管理方式就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理
  
  

  

2.2.1、针对内置类型

  1)、new、delete操作符针对内置类型进行动态内存管理的基本用法

   1、首先要明确的是,new、delete是关键字。而C语言中那套动态内存管理依赖的是函数。

在这里插入图片描述如何开辟空间?

void Test()
{// 动态申请一个int类型的空间
	int* ptr4 = new int;
	// 动态申请一个int类型的空间并初始化为10
	int* ptr5 = new int(10);
	// 动态申请10个int类型的空间
	int* ptr6 = new int[10];
}

int main(void)
{Test();
	return 0;
}

在这里插入图片描述
   如何使用new开辟数组类型的空间?(即一次性同时生成多个类型相同的元素)
   该问题C++98不支持,C++11才支持,书写方法如下:

void Test()
{//C++11:
	int* ptr1 = new int[10]{1,2,3 };
}

int main(void)
{Test();
	return 0;
}

在这里插入图片描述

  
  

在这里插入图片描述如何释放空间?

void Test()
{// 动态申请一个int类型的空间
	int* ptr4 = new int;
	// 动态申请一个int类型的空间并初始化为10
	int* ptr5 = new int(10);
	// 动态申请10个int类型的空间
	int* ptr6 = new int[10];
	delete ptr4;
	delete ptr5;
	delete[] ptr6;//单个对象直接delete,若是多个对象则使用[ ]


	//C++11:
	int* ptr1 = new int[10]{1,2,3 };
	delete[] ptr1;//单个对象直接delete,若是多个对象则使用[ ]
}

int main(void)
{Test();
	return 0;
}

  总结:针对内置类型,new、deletemalloc、free没有本质的区别,只有用法的区别。new、delete用法相对简化了。
  
  
  

2.2.2、针对自定义类型

  1)、new、delete针对自定义类型进行动态内存管理的基本方法

class A
{public:
	A(int a = 0)
		: _a(a)
	{cout<< "A():"<< this<< endl;
	}

	~A()
	{cout<< "~A():"<< this<< endl;
	}

private:
	int _a;
};



int main()
{// 1、堆上申请空间
	A* p1 = (A*)malloc(sizeof(A));
	if (p1 == NULL)
	{perror("malloc fail");
		return 0;
	}

	// 1、释放空间
	free(p1);

	// 1、堆上申请空间  2、调用构造函数初始化
	A* p2 = new A;
	A* p2 = new A(10);

	// 1、调用析构函数清理对象中资源 2、释放空间
	delete p2;

	cout<< endl<< endl;

	A* p3 = new A[2];
	delete[] p3;

	//C++11支持
	A* p3 = new A[2]{1,2 };
	A* p3 = new A[2]{A(1), A(2) };

	// 结论:new/delete 是为自定义类型准备的。
	// 不仅在对申请出来,还会调用构造和析构初始化和清理

	return 0;
}

在这里插入图片描述malloc、free对自定义类型

   1、对自定义类型使用malloc、free申请和释放空间:并不会对自定义类型初始化。

// 1、堆上申请空间
	A* p1 = (A*)malloc(sizeof(A));
	if (p1 == NULL)
	{perror("malloc fail");
		return 0;
	}
		// 1、释放空间
	free(p1);

在这里插入图片描述new、delete对自定义类型

   1、使用new动态申请自定义类型,会调用默认构造函数初始化。
   2、若类不提供默认构造,若要编译通过,则需要自己传参。

// 1、堆上申请空间  2、调用构造函数初始化
	A* p2 = new A;//这种写法需要有默认构造
	A* p2 = new A(10);//如果没有默认构造则需要我们手动传参
	delete p2;

   3、相比于free,delete会调用析构函数清理对象中的资源(但是否需要这是取决于析构函数本身)

A* p3 = new A[2];
	delete[] p3;

  4、若是没有默认构造,多个自定义类型需要传参的写法(一种是构造(隐式类型)、一种是拷贝构造:

//C++11支持
	A* p3 = new A[2]{1,2 };
	A* p3 = new A[2]{A(1), A(2) };	

	delete[] p3;

   结论:new/delete 是为自定义类型准备的。不仅在对申请出来,还会调用构造和析构初始化和清理.

  
  2)、delete、new构造、析构顺序探索
  对于多个自定义类型,此处需要注意构造和析构的顺序,以及在堆上的地址排序。

class A
{public:
	A(int a = 0)
		: _a(a)
	{cout<< "A():"<< this<< endl;
	}

	~A()
	{cout<< "~A():"<< this<< endl;
	}

private:
	int _a;
};
int main()
{A* p1 = new A[10];
	cout<< endl;
	delete[] p1;
	return 0;
}

在这里插入图片描述

  
  

2.2.3、new申请动态内存失败后如何处理
int main()
{//C: 失败返回NULL
	char* p1 = (char*)malloc(1024u * 1024u * 1024u * 2 - 1);//u表示无符号
	//cout<< p1<< endl;//cout自动识别类型,会先去解引用字符指针,而非取地址。
	printf("%p\n", p1);

	//C++: new失败,不需要检查返回值,他失败是抛异常
	try
	{//申请空间失败的一种写法:
		char* p2 = new char[1024u * 1024u * 1024u * 2 - 1];//此处失败会直接跳转后续catch部分,不会指向下一行。
		printf("%p\n", p2);
		///申请空间失败的另一种方法:
		size_t n = 0;
		while (1)
		{	char* p2 = new char[1024];
			++n;

			printf("%p->[%d]\n", p2, n);
		}
	}
	catch (const exception& e)//捕获异常
	{cout<< e.what()<< endl;
	}

	return 0;
}

  
  
  

2.2、operator new与operator delete全局函数 2.2.1、关于new、delete功能的底层原理

   1、new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是 系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过 operator delete全局函数来释放空间。
  2、而operator new 实际也是通过malloc来申请空间,如果 malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施 就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
  
  

2.2.2、重载operator new与operator delete

  1、 一般情况下不需要对 operator new 和 operator delete进行重载,除非在申请和释放空间时候有某些特殊的需求。比如:在使用new和delete申请和释放空间时,打印一些日志信息,可以简单帮助用户来检测是否存在内存泄漏。

  2、关于专属的operator new的实现:可用于频繁调用new需要提高效率的情况

// new ->operator new + 构造函数
// 默认情况下operator new使用全局库里面
// 每个类可以去实现自己专属operator new  new这个类对象,就会调自己实现这个operator new

// 实现一个类专属的operator new  -- 了解一下
struct ListNode
{int _val;
	ListNode* _next;

	// 内存池
	static allocatoralloc;//静态成员变量

	void* operator new(size_t n)//重载专属的operator new
	{cout<< "operator new ->STL内存池allocator申请"<< endl;
		void* obj = alloc.allocate(1);
		return obj;
	}

	void operator delete(void* ptr)//重载专属的operator delete
	{cout<< "operator delete ->STL内存池allocator申请"<< endl;

		alloc.deallocate((ListNode*)ptr, 1);
	}

	struct ListNode(int val)
		:_val(val)
		, _next(nullptr)
	{}
};

// allocator以后会讲,现在先会用即可
allocatorListNode::alloc;//类外定义

int main()
{// 频繁申请ListNode. 想提高效率 -- 申请ListNode时,不去malloc,而是自己定制内存池
	ListNode* node1 = new ListNode(1);//new相对于malloc简化
	ListNode* node2 = new ListNode(2);
	ListNode* node3 = new ListNode(3);

	delete node1;
	delete node2;
	delete node3;

	return 0;
}

  
  

2.2.3、new、delete实现原理总结

在这里插入图片描述
  
  

2.3、定位new 2.3.1、是什么和为什么

  1)、场景引入:

class A
{public:
	A(int a = 0)
		: _a(a)
	{cout<< "A():"<< this<< endl;
	}

	~A()
	{cout<< "~A():"<< this<< endl;
	}

private:
	int _a;
};

int main()
{A* p1 = new A;

	A* p2 = (A*)malloc(sizeof(A));
	if (p2 == nullptr)
	{perror("malloc fail");
	}
	return 0;
}

  如上述代码:有时我们需要对已经定义的类进行初始化。而我们知道,类初始化需要调用构造函数,目前已知的两种构造函数调用方式是,①在定义类时会调用构造函数初始化成员;②通过new开辟自定义类型也会进过构造函数。
  那么,针对上述p2这样已经申请好的类,成员变量私有情况下,如何为它初始化?

  
  2)、是什么和怎么用:
  概念:
  定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
  使用格式:
  new (place_address) type或者new (place_address) type(initializer-list)
  ①place_address必须是一个指针
  ②initializer-list是类型的初始化列表
  
   演示:

new(p2)A;
	new(p2)A(10);
class A
{public:
	A(int a = 0)
		: _a(a)
	{cout<< "A():"<< this<< endl;
	}

	~A()
	{cout<< "~A():"<< this<< endl;
	}

private:
	int _a;
};

int main()
{A* p1 = new A;

	A* p2 = (A*)malloc(sizeof(A));
	if (p2 == nullptr)
	{perror("malloc fail");
	}
	new(p2)A;//方法一
	new(p2)A(10);//方法二

	return 0;
}

  
  3)、为什么用:
  使用场景:
  定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
  
  
  

3、模板初阶 3.1、泛型编程

  1)、关于为什么要有泛型编程的引入说明:

void Swap(int& left, int& right)
{int temp = left;
	left = right;
	right = temp;
}

void Swap(double& left, double& right)
{double temp = left;
	left = right;
	right = temp;
}

void Swap(char& left, char& right)
{char temp = left;
	left = right;
	right = temp;
}

int main()
{int i = 1, j = 2;
	double x = 1.1, y = 2.2;
	Swap(i, j);
	Swap(x, y);

	char m = 'A', n = 'B';
	Swap(m, n);

	return 0;
}

   类似于上述Swap交换函数,虽然我们也可以通过typedef重命名来更换类型,但如果出现多个类型同时需要使用Swap函数时,typedef仍旧不能很好的解决问题。
在这里插入图片描述

   因此,我们提出了泛型编程。
  泛型编程: 编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

  
  
  
  

3.2、函数模板 3.2.1、函数模板的概念和基本使用方式

  template
  返回值类型 函数名(参数列表){}

   实例演示:

templatevoid Swap(T& left, T& right)
{T tmp = left;
	left = right;
	right = tmp;
}

   1、typename后面类型名字T是随便取,Ty、K、V,一般是大写字母或者单词首字母大写
  2、T 代表是一个模板类型(虚拟类型)
  3、注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)

template//此处typename与class只有一些细微区别,后面再讲述
void Swap(T& left, T& right)
{T tmp = left;
	left = right;
	right = tmp;
}

  演示如下:此处有一个问题,我们用不同类型的变量去调用这个函数模板时,所生成的Swap函数是不是同一个?
在这里插入图片描述

  
  

3.2.2、函数模板底层原理

  1)、问题引入:
  接上文:
在这里插入图片描述

  
  2)、总述:
在这里插入图片描述

  
  

3.2.3、函数模板的实例化:隐式、显式等

  1)、函数模板实例化·隐式实例化中一个容易出错的问题

templateT Add(const T& left, const T& right)
{return left + right;
}

int main()
{int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	Add(a1, a2);//right
	Add(d1, d2);//right

	Add(a1, d1);//error

	return 0;
}

在这里插入图片描述

  隐式实例化中编译报错的场景之一:
  报错原因并非不能隐式类型转换(事实上C++沿袭C,不用模板我们自己写Add时是成功的),此处报错的真正原因在于推演实例化报错。即无法确定需要转换到哪个类型。
  
  
  2)、如何解决隐式实例化中这类问题?

在这里插入图片描述解决方案一:强制类型转换

  演示如下:

templateT Add(const T& left, const T& right)
{return left + right;
}

int main()
{//编译器自动推演,隐式实例化
	cout<< Add(1, 2)<< endl;
	cout<< Add((int)1.1, 2)<< endl;
	cout<< Add(1.1, (double)2)<< endl;

	return 0;
}

在这里插入图片描述

  
  

在这里插入图片描述解决方案二:显示实例化

  显式实例化:在函数名后的<>中指定模板参数的实际类型

templateT Add(const T& left, const T& right)
{return left + right;
}

int main()
{//显式实例化:在函数名后的<>中指定模板参数的实际类型
	cout<< Add(1, 2)<< endl;
	cout<< Add(1.1, 2)<< endl;
	cout<< Add(1.1, 2)<< endl;

	return 0;
}

  1、此处模板类型有几个,显示实例化时,实际类型就要写几个。
  2、如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

在这里插入图片描述

  
  
  

在这里插入图片描述解决方案三:使用多个模板类型

templateT1 Add(const T1& left, const T2& right)
{return left + right;
}

int main()
{cout<< Add(1, 2)<< endl;
	cout<< Add(1.1, 2)<< endl;
	cout<< Add(2, 1.1)<< endl;//要注意理解此处细节:参与Add时仍旧会发生隐式类型转换为double,只是返回类型为int
	return 0;
}

在这里插入图片描述

  
  
  3)、一些必须显示实例化的场景说明

templateT* Func(int n)
{T* a = new T[n];
	return a;
}

class A{};

int main()
{// 必须显示实例化才能调用
	Func(10);//此处是因为传入的参数为int型,而实际T需要返回类型为类A,因此在此处指定实际类型为A。

	return 0;
}

  
  

3.2.4、模板参数的匹配原则

  1)、关于函数模板、非模板函数同时存在时,如何调用的问题?
  回答:如何有匹配的非模板函数,率先调用该函数,若没有,则调用模板函数。

  

在这里插入图片描述演示实例一:

   一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。

// 专门处理int的加法函数
int Add(int left, int right)
{return left + right;
}
// 通用加法函数
templateT Add(T left, T right)
{return left + right;
}
void Test()
{Add(1, 2); // 有匹配的非模板函数,不会调用模板
	Add(1.1, 2.2);// 没有匹配的非模板函数,会去调用模板
	Add(1, 2); // 调用编译器特化的模板
}

int main()
{Test();
	return 0;
}

  

在这里插入图片描述演示实例二:

   对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。

// 专门处理int的加法函数
int Add(int left, int right)
{return left + right;
}
// 通用加法函数
templateT1 Add(T1 left, T2 right)
{return left + right;
}
void Test()
{Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}

int main()
{Test();
	return 0;
}

  
  

3.3、类模板 3.3.1、为什么和是什么

  1)、为什么需要类模板的引入?
在这里插入图片描述

class Stacki
{private:
	int* _a;
	int top;
	int capacity;
};

class Stackc
{private:
	char* _a;
	int top;
	int capacity;
};

int main()
{Stackc st1; // char
	Stacki st2; // int

	return 0;
}
typedef char STDataType;//此处修改类型
class Stack
{private:
	STDataType* _a;
	int top;
	int capacity;
};

int main()
{Stack st1; // char
	Stack st2; // char

	return 0;
}

  
  
  2)、类模板如何使用?
   类模板格式说明:

templateclass 类模板名
{// 类内成员定义
};

  
  3)、以类模板定义Stack举例说明使用细节:各类知识杂糅与理解
  总体演示:

template//用模板定义一个类
class Stack
{public:
	//构造函数
	Stack(size_t capacity = 4)//缺省值
		: _a(nulltpr)//初始化列表:默认先处理为这样子
		, _top(0)
		, _capacity(0)
	{if (capacity >0)//虽然给了缺省值,但也有可能实参传递进来的是0
		{	//为_a开辟空间,此处使用了new,注意new的用法以及new多个空间时[]中传入的数据
			_a = new T[capacity];//此处不需要检查new是否失败,因为可以使用抛异常try\catch
			_capacity = capacity;
			_top = 0;
		}
	}
	private:
	T* _a;
	size_t _top;
	size_t _capacity;
};
int main()
{//用同一个类模板显示实例化
	//不过Stack、Stack是不同的两个类型
	Stackstr1;
	Stackstr2;

	//关于缺省值:如果我们能具体知道capacity值,就能这样直接传入
	//若不知晓,就可以使用缺省值中给定的。
	Stackstr3(100);

	//关于初始化列表中的_capacity是为了处理这样的情况:
	Stackstr4(0);
	//若没有它,上述构造函数中进不了if语句,而_capacity没有初始化,
	//那么得到的str4实际上_capacity得到的是默认随机值,
	//而我们插入数据时扩容却是看_top==_capacity的
	//这样就出了问题
}

  细节解释一:自定义类型的模板定义需要显式实例化:即这里需要理解模板定义出来的类型T需要用到哪里。

//用同一个类模板显式实例化
	//不过Stack、Stack是不同的两个类型
	Stackstr1;
	Stackstr2;

  细节解释二:初始化列表处,_capacity(0)这些是否要处理?

Stack(size_t capacity = 4)//缺省值
		: _a(nulltpr)//初始化列表:
		, _top(0)
		, _capacity(0)
		{}

  回答:需要。构造函数中我们给予的只是缺省值,如果显示穿参并且参数为0时,会导致_capacity处为随机值,那么后续会遇到各种麻烦。

//关于初始化列表中的_capacity是为了处理这样的情况:
	Stackstr4(0);
	//若没有它,上述构造函数中进不了if语句,而_capacity没有初始化,
	//那么得到的str4实际上_capacity得到的是默认随机值,
	//而我们插入数据时扩容却是看_top==_capacity的
	//这样就出了问题

  细节解释三:如果不在初始化列表给值,也可以在private成员变量中给,即使用C++打的补丁,但本质上是一样的。

private:
	T* _a =nullptr;
	size_t _top =0;
	size_t _capacity =0;

  
  

3.3.2、模板实例化说明

  模板不支持声明和定义跨文件分离。若是觉得放在类中形成内联不方便,则可在同文件中进行声明和定义分离。
  同文件中的声明和定义分离,注意类模板的写法。
在这里插入图片描述

  
  

3.3.3、以Stack的实现演示模板使用

  

templateclass Stack
{public:
	//构造函数
	Stack(size_t capacity = 4)//缺省值
		:_a(nulltpr)//初始化列表:默认先处理为这样子
		, _top(0)
		, _capacity(0)
	{if (capacity >0)//虽然给了缺省值,但也有可能实参传递进来的是0
		{	//为_a开辟空间,此处使用了new,注意new的用法以及new多个空间时[]中传入的数据
			_a = new T[capacity];//此处不需要检查new是否失败,因为可以使用抛异常try\catch
			_capacity = capacity;
			_top = 0;
		}
	}

	//析构函数
	~Stack()
	{//需要手动释放动态内存空间
		delete[] _a;//使用delete时需要注意加括号
		//后续置空这个步骤不影响
		_a = nullptr;
		_capacity = _top = 0;
	}

	//拷贝构造:此处设计深浅拷贝,后续讲解


	void Push(const T& x);//此处再次体现泛型模板,因为插入数据类型为T


	//删除
	void Pop()
	{assert(_top >0);
		--_top;
	}

	//判空
	bool Empty()
	{return _top == 0;
	}

	//取栈顶元素
	const T& Top()
	{//此处_a中的数据属于堆上动态开辟的,因此可以使用引用传值返回
	//此处加入const是为了防止引用返回后将栈顶元素修改

		assert(_top >0);
		return _a[_top - 1];
	}

private:
	T* _a;
	size_t _top;
	size_t _capacity;
};




//栈顶插入:
//此处再次体现泛型模板,因为插入数据类型为T
{//检查扩容
	//有两种可能性:第一,空间已满;第二,空间未开辟
	if (_top == _capacity)
	{// 1、开新空间
		// 2、拷贝数据
		// 3、释放旧空间
		size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;

		//扩容需要重新开辟空间,以前我们学习的是malloc和realloc,能原地扩容或者系统自己解决
		//现在我们使用new、delete,其不能自动解决空间问题,因此需要我们手动处理
		T* tmp = new T[newcapacity];
		//引出问题:为什么C++中我们推荐使用new、delet?
		//回答:此处使用的是类模板,其模板类型T有可能是自定义类型
		//malloc对自定义类型在申请空间时不会初始化。

		//开了新空间,需要对旧空间进行处理
		if (_a)//这里是用来检查是否为第一次扩容,因为首次扩容就不存在搬动空间问题
		{	//要将旧空间中的数据拷贝到新空间里
			memcpy(tmp, _a, sizeof(T) * _top);//注意memcpy的使用参数含义
			//释放旧空间
			delet[] _a;
		}
		//改变指向关系
		_a = tmp;
		_capacity = newcapacity;
	}

	_a[_top] = x;
	++_top;
}

  
  

  
  
  

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


网站标题:【ONE·C++||内存管理和模板初阶】-创新互联
网站地址:
http://pwwzsj.com/article/codgoo.html