内存管理、模板(内存分布,内存管理方式:new、delete && malloc、free)

  提到代码,我们往往会有一个疑问:计算机中的内存有划分之说吗?如果有,那代码存储在计算机中的哪些区域?就像日常生活中我们对于土地的划分一样,根据外部环境、市场、交通等因素,不同的区域放置不同的资源。计算机也是一样,有着自己的资源分配规则,不同的区域放置不同的资源(变量)。那我们如何通过编程语言去合理利用这些资源呢?这篇文章会告诉你内存的栈区、堆区、常量区...存储何种变量,并且从C和C++这两种编程语言去讲解如何开辟与释放空间以及进一步了解C++中 操作符:new && delete,函数:operator new && operator delete,还有模板的讲解、使用方法。

目录

一、C/C++内存分布

 1.内存分布精准打击:

 2.解析+内存分布图:

二、C/C++内存管理方式

 1.C内存管理方式:malloc、calloc、realloc  --  free

 2.C++内存管理方式:

  (1)操作符new && delete:

    ①内置类型:

     ②自定义类型:

  (2)函数operator new &&operator delete:

 3.进一步理解new 和 delete:

  (1)内置类型(char、int、long ....):

  (2)自定义类型:

三、定位new表达式

 1.概念:

 2.用法:

 3.代码举例:

四、模板

 1.函数模板:

  (1)概念:

  (2)格式:

   (3)详解:

   (4)函数模板的实例化:

    ①隐式实例化:

    ②显式实例化:

   (5)函数模板的匹配原则:

 2.类模板:

  (1)概念:

  (2)格式:

  (3)例子:

  (4)类模板的实例化:


一、C/C++内存分布

 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);
}

 2.解析+内存分布图:

  ①globalVar(全局)、staticGlobalVar(静态变量)、staticVar(静态变量)、localVar(局部变量)、num1(数组首元素地址 -- 指针)

  ②char2(指针)、*char2(字符串首元素,字符串存储栈区)、pChar3(指针)、*pChar3(字符串首元素,字符串存储常量区)、ptr1(指针

  sizeof计算大小、strlen计算字符串长度

二、C/C++内存管理方式

 1.C内存管理方式:malloc、calloc、realloc  --  free

 2.C++内存管理方式:

  按道理来说,C语言的内存管理方式C++也可以适用,但是既然C++存在那么就一定有更优越于C语言的地方,所以,操作符new、delete横空出世

  (1)操作符new && delete:

   *使用方法:①指针变量 = new 类型

                      ②指针变量 = new 类型(对应参数)   --------   初始化

                        delete 指针变量

                      ③指针变量 = new 类型[ 数量 ]   -----        ---   开辟连续空间(等同于数组)

                        delete[] 指针变量

     *范例:

    ①内置类型:
void test()
{
  //创建指针
  int* ptr4 = new int;
  int* ptr5 = new int(2);
  int* ptr6 = new int[6];
  //释放空间
  delete pt4;
  delete pt5;
  delete[] pt6;
}
     ②自定义类型:
class A
{
public:
  A(int a = 0)
  :_a(a)
  {
    cout<<”A()”<<endl;
}
~A()
{
  cout<<”~A()”<<endl;
}
private:
  int _a;
}
int main()
{
  A* ptr3 = (A*)malloc(sizeof(A));
  A* ptr4 = new A(1);
  free(ptr3);
  delete ptr4;

  A* ptr1 = (A*) malloc(sizeof(A) * 10);
  A* ptr2 = new A[10];
  free(ptr1);
  delete[] ptr2;
  return 0;
}

   补充说明:在使用操作符new和delete 操作符 开辟/释放 空间时,会调用相对应的 构造/析构函数,而malloc和free则不会。

  (2)函数operator new &&operator delete

   *operator new 和 operator delete 是2个全局函数,上文提到的new和delete2个操作符就是在底层通过调用这2个函数来实现 申请空间 && 释放空间 

   *operator new 和 operator delete 实际上也是通过 malloc 和 free 来 申请 和 释放空间的,区别就是:malloc申请失败则返回NULLoperator new 申请失败则会执行用户提供的空间不足对应措施,若该措施存在则继续执行,否则抛异常。operator delete 则是通过free来释放空间。

 3.进一步理解new 和 delete:

  通过上面的学习,我们已经大概知道这2个操作符和malloc、free的区别了,但是,上述的区别实在是让人觉得没有说服力,难道仅仅是因为使用时稍微简单、方便点就创造了这2个操作符?亦或者:难道就因为 operator new 申请空间失败时可以执行对应措施就创建了这个函数?请听我针对空间存储的数据类型来解释:

  (1)内置类型(char、int、long ....):

   对于内置类型来说,使用malloc 还是new开辟空间存放并无区别,因为这些类型不会再衍生出其他空间,使用free可以直接将其开辟的空间释放。

  (2)自定义类型:

   我们知道自定义类型的基本构造:构造函数析构函数 是必须存在的!问题的关键恰恰就在这里,使用new和delete可以调用 构造函数和析构函数,而malloc和free并不可以,从开辟空间的角度来说new和malloc 并无二异,但是如果释放空间就会显现出异常了:free仅仅会释放指向若干个存储自定义类型的空间,并不能将自定义类型内成员函数开辟的空间释放,如果是用free释放的话会有内存泄漏的风险!而delete会调用析构函数,因此会将自定义类型内成员函数开辟的空间释放。

   上述就是new && delete 与 malloc && free 比较核心的区分,除此之外,它们之间还有使用方法上的不同:

   如:

    开辟数组:①new:类型* ptr1 = new[n];

                       ②malloc:类型* ptr2 = (类型*)malloc(所需开辟空间的大小)

       使用操作符new只需要知道开辟空间的类型,而使用malloc则需要知道开辟空间的大小!!!

三、定位new表达式

 1.概念:

  定位new 是在已分配的空间上调用构造函数初始化对象

 2.用法:

  new(place_address)type  /   new(place_address)type((initializer-list)

 3.代码举例:

class A
{
public:
  A(int a = 0)
  :_a(a)
  {
    cout<<”A()”<<endl;
}
~A()
{
  cout<<”~A()”<<endl;
}
private:
  int _a;
}
int main()
{
  //首先开辟一个A类型大小的空间赋值给指针ptr1,此时ptr1还不是一个对象
  A* ptr1 = (A*)malloc(sizeof(A));
  //使用定位new在ptr1指向的空间上创建一个对象
  new(ptr1)A;   //若调用析构函数需要传参,则此处要传递参数
  //释放空间
  Ptr1->~A();
  free(ptr1);

  //构造函数需要传参
  A* ptr2 = (A*)malloc(sizeof(A));
  new(ptr2)A(2);
  //释放
  ptr2->~A();
  free(ptr2);
}

四、模板

 大家一定遇到过这样一种情况:使用一个函数达到某种效果,但是当遇到想让函数实现的效果相同但参数类型不同这种情况时,大家只能复制写好的函数,然后逐个修改类型。这样做看似是重复性的机械劳动,但是又不可省略。那么问题来了:如何省略这样机械性的重复呢?答案是:不可能!但是!人可以省略这个重复性的过程,让编译器去完成!这就是模板。

 1.函数模板:

  (1)概念:

    函数模板代表的 效果、参数个数 相同的所有函数,在使用时会被参数化,根据传入的参数创造对应的类型版本。

  (2)格式:
tymplate<typename T1, typename T2.....>
返回值 函数名(参数列表)
{
 
}

   实例:

 tymplate<typename T>
 Void Swap(T& p1,T& p2)
 {
  T tmp;
  tmp = p1;
  p1 = p2;
  p2 = tmp;
}

                (typename 也可以用 class 代替,但是不能用struct来代替class!!!)

   (3)详解:

     函数模板其实并没有省略掉创建函数这个环节,只是不需要我们认为的去编写只是参数类型不同的函数了,编译器会替我们完成这个环节。

   (4)函数模板的实例化:

     函数模板分为 隐式实例化 和 显式实例化

    ①隐式实例化:

       编译器通过对参数类型的判定去选择创造对应的函数,但是不能产生歧义!如下:

 tymplate<typename T>
 Void Swap(T& p1,T& p2)
 {
  T tmp;
  tmp = p1;
      p1 = p2;
      p2 = tmp;
}
int main()
{
  int a = 10;
  double b = 5.2;
  Swap(a,b); // 这样是错误的,因为编译器首先读取参数a,判定参数类型//为 int ,
             //但是读取参数b时,判定参数又为double,因此编//译不通过解决办法:
             // ①自行强转 ②显式实例化(见下文)
  //用户自行强转
  Swap(a,(int)b)
  return 0;
}
    ②显式实例化:

       在函数名后的<>中说明参数类型

int main()
{
  int a = 10;
  double b = 5.2;
  //显式实例化
  Swap<int>(a,b)
  return 0;
}
   (5)函数模板的匹配原则:

     对于非模板函数和同名函数模板,如果其他条件都相同,那么将不会通过函数模板产生一个实例,而是直接调用此函数。如果函数模板更符合条件,则通过函数模板产生实例(择优录取

       (模板函数不会进行类型自动转换,但是普通函数可以!

 2.类模板:

  (1)概念:

   既然存在有函数模板,那么也就一定存在类模板,所谓的类模板就是说:不同的类除了类中参数的类型不同外,其他全部一样。这时就可以使用类模板。

  (2)格式:
template(typename T1, typename T2...)
class 类模板名
{
  ...
}
  (3)例子:
//类模板(顺序表)
template<class T>
class Vector
{
public:
	//构造函数
	Vector(size_t capacity = 10)
		:_pData(new T[_capacity])
		, _size(0)
		, _capacity(capacity)
	{
		cout << "Vector()" << endl;
	}
	
	//析构函数 (类中声明 类外定义)
	~Vector();

private:
	T* _pData;
	size_t _size;
	size_t _capacity;
};

//析构函数 (需要先写出模板参数列表)
template<class T>
Vector<T>::~Vector()
{
	if (_pData)
	{
		delete[] _pData;
		_size = _capacity = 0;
	}
	cout << "~Vector()" << endl;
}


int main()
{
    //类模板正确使用方法
	Vector<int> S1;
	Vector<double> S2;
}
  (4)类模板的实例化:

   我们上述所编写的 Vector 其实并不是类型,它只是类名,正确将它改为类型还需要在后面加 <实例化类型>,这时,Vector<实例化类型> 才是一个类型,才可以创建对象。

  以上就是本篇文章的全部内容啦!希望这篇文章可以帮助你更好的学习与了解 内存分布,内存管理方式:new、delete && malloc、free等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值