【C++学习手札】new和delete看这一篇就够了!

简介: 【C++学习手札】new和delete看这一篇就够了!

🌞引入—从C语言malloc、free到C++new、delete

      各位请先看下面这段代码:

void test1()//C语言动态管理空间
{
  int* p = NULL;
  p = (int*)malloc(sizeof(int));
  *p = 100;
  printf("%d\n", *p);
  free(p);
}
void test2()//C++动态管理空间
{
  int* p = NULL;
  p = new int(100);
  printf("%d\n", *p);
  delete p;
}

这两段代码实现的功能是一样的。通过观察以及比对,我们很容易就能发现malloc和free分别对应着new和delete。乍一看,malloc、free和new、delete好像没什么区别?没错,他们的功能都是一样的,都是用来动态管理空间的。但是,如果细细观察,你会发现他们区别可大了!

    注意:

              1、new不需要强制类型转换。

               2、new可以在开辟空间时,可以同时初始化空间内容。

  \(^o^)/~ok,基本的引入就到这吧,现在我们进入正题,对于new、delete详解~


🌔 一、new和delete语法定义

       new的语法定义

//动态分配一个空间时
指针 = new 指针对应的类型;
//例1
int* ptr = new int;//在堆上分配一个整数的内存,并将其地址存储在指针ptr中
//申请多个空间时或者用于动态分配数组的内存时
指针 = new 指针对应的类型[申请的大小];
//例2
int* arr = new int[5];//在堆上分配一个包含5个整数的数组,并将其起始地址存储在指针arr中

delete的语法定义

//当只开辟了一个空间时
delete 所要释放开间的指针
//例1
delete ptr;//释放空间
//申请多个空间时或者用于动态分配数组的内存时
delete 所要释放开间的指针
delete[] 所要释放空间的指针 //此释放该指针开辟的所有空间
//例2
delete arr;//可能只释放首个空间->不同编译器所为不同
delete[] arr;//全部释放

      特别注意:如果new和delete应当采用相同形式,详见本文末。

       new的初始化

//动态分配一个空间时
指针 = new 指针对应的类型(对应类型数值);
//例1
int* ptr = new int(100)//初始化空间值为100
//申请多个空间时或者用于动态分配数组的内存时
指针 = new 指针对应的类型[申请的大小]{数值(用,隔空)};
//例2
int* arr = new int[5]{1,2,3,4,5};//初始化5个空间值依次为1,2,3,4,5

 栗子:

void test2()//C++动态管理空间
{
  int* p = NULL;
  p = new int(100);
  printf("%d\n", *p);
  delete p;
  int* q = NULL;
  int* z = NULL;
  q = new int[5] {10, 20, 30, 40, 50};
  z = new int[5] {0};
  for (int i = 0; i < 5; i++)
  {
    cout << q[i] << " ";
  }
  cout << endl;
  for (int i = 0; i < 5; i++)
  {
    cout << z[i] << " ";
  }
  delete[]z;
  delete[]q;
}

🌕二、给类对象申请空间(为什么说C++中要用new和delete)

       用malloc和用new给类申请空间的区别

       请看下面这段代码~

class A
{
public:
  int num;
public:
  A()
  {
    num = 200;
    cout << "构造函数" << endl;
  }
  ~A()
  {
    cout << "析构函数" << endl;
  }
};
void test()
{
  A* p = (A*)malloc(sizeof(A));
  p->num = 100;
  cout << "malloc:num=" << p->num << endl;
  free(p);
  cout << endl;
  A* q = new A;
  cout << "new:num=" << q->num << endl;
  delete q;
}

  以下为该段代码的结果:

       很明显的能看到,如果我们使用malloc开辟空间以及free来释放空间,类中最经典的构造函数和析构函数都是没有被调用的!然而,使用new来申请空间 如果申请成功 就会自动调用 对应类的构造函数,在用delete释放空间时会自动调用析构函数!因此,在C++中,我们使用new和delete会比malloc和free安全的多!

    new申请对象数组

        请看下面这段代码~

class A
{
public:
  int num;
public:
  A()
  {
    num = 100;
    cout << "无参构造函数num="<<num << endl;
  }
  A(int n)
  {
    num = n;
    cout << "有参构造函数num="<<num << endl;
  }
  ~A()
  {
    cout << "析构函数num="<<num << endl;
  }
};
void test()
{
  cout << "无参:" << endl;
  A* arr1 = new A[5];
  delete[]arr1;
  cout << endl;
  cout << "有参:" << endl;
  A* arr2 = new A[5]{A(1),A(2),A(3),A(4),A(5)};
  delete[] arr2;
}

 以下为该段代码的结果:

       以上的代码分别为利用无参构造函数和有参构造函数构造的对象数组,从以上例子我们也可总结出以下几点要点:

       1、类对象数组本质是数组 只是数组的每个元素是类的对象。


       2、如果想让对象数组中的元素调用有参构造 必须人为使用 有参构造初始化。

       3、初始化的元素 调用有参构造 没有初始化的 调用无参构造。


       4、当创建一个对象数组的时候, 必须对数组中的每一个对象调用构造函数, 除了在栈

       上可以聚合初始化, 必须提供一个默认的构造函数。

这里建议回顾回顾类的知识:  构造函数与析构函数 (这是链接,快点!)

在对象数组中:

       每个元素自动调用构造和析构函数,而他的构造顺序和析构顺序入下图所示:

       构造按照入上文例子中有参构造,从左往右,也就是说谁先定义谁先构造,而析构则是相反,谁最后定义谁最先析构!


🌖三、一些注意事项

       delete void*可能会出错

      如果对一个 void*指针执行 delete 操作, 这将可能成为一个程序错误, 除非指针指
向的内容是非常简单的, 因为它将不执行析构函数.以下代码未调用析构函数, 导致可用内存减少。
所以尽量不要用delete释放void *!

栗子:

class Person {
public:
  Person(char* name, int age) 
  {
    pName = (char*)malloc(sizeof(name));
    strcpy(pName, name);
    mAge = age;
  }
  ~Person() {
    if (pName != NULL) {
      delete pName;
    }
  }
public:
  char* pName;
  int mAge;
};
void test() {
  char arr[] = "john";
  void* A = new Person(arr, 22);
  delete A;
}

特别注意:malloc、free和new、delete 不可以混搭使用!

       使用 new 和 delete 采用相同形式

       请仔细看下面这段代码~

    Person* person = new Person[10];
    delete person;

       以上代码有什么问题吗?

       这里使用了两个编译器来对该代码进行运行,分别出现了以下错误:vs 下直接中断、 qt 下析构函数调用一次。

       使用了 new 也搭配使用了 delete, 问题在于 Person 有 10 个对象, 那么其他 9 个对象可能没有调用析构函数, 也就是说其他 9 个对象可能删除不完全, 因为它们的析构函数没有被调用。 我们现在清楚使用 new 的时候发生了两件事: 一、 分配内存; 二、 调用构造函数, 那么调用 delete 的时候也有两件事: 一、 析构函数; 二、 释放内存。 那么刚才我们那段代码最大的问题在于: person 指针指向的内存中到底有多少个对象, 因为这个决定应该有多少个析构函数应该被调用。 换句话说, person指针指向的是一个单一的对象还是一个数组对象, 由于单一对象和数组对象的内存布局是不同的。 更明确的说, 数组所用的内存通常还包括“数组大小记录”, 使delete 的时候知道应该调用几次析构函数。 单一对象的话就没有这个记录。

  单一对象和数组对象的内存布局可理解为下图:

       本图只是为了说明, 编译器不一定如此实现, 但是很多编译器是这样做的。 当我们使用一个 delete 的时候, 我们必须让 delete 知道指针指向的内存空间中是否存在一个“数组大小记录”的办法就是我们告诉它。 当我们使用 delete[ ], 那么 delete就知道是一个对象数组, 从而清楚应该调用几次析构函数。 结论: 如果在 new 表达式中使用[ ], 必须在相应delete 表达式中也使用[ ].如果在 new 表达式中不使用[], 一定不要在相应的 delete 表达式

中使用 [ ]。


 感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

相关文章
|
1月前
|
C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
1月前
|
编译器 C++ 开发者
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
1月前
|
存储 C语言 C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(一)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
3月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
81 0
|
26天前
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
|
1月前
|
程序员 C语言 C++
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
68 1
|
1月前
|
Java 编译器 C++
c++学习,和友元函数
本文讨论了C++中的友元函数、继承规则、运算符重载以及内存管理的重要性,并提到了指针在C++中的强大功能和使用时需要注意的问题。
21 1
|
2月前
|
C++
C++(十九)new/delete 重载
本文介绍了C++中`operator new/delete`重载的使用方法,并通过示例代码展示了如何自定义内存分配与释放的行为。重载`new`和`delete`可以实现内存的精细控制,而`new[]`和`delete[]`则用于处理数组的内存管理。不当使用可能导致内存泄漏或错误释放。
|
5天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
25 5
|
11天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
40 4