从C语言到C++⑨(第三章_C&C++内存管理)详解new和delete+面试题笔试题(下)

简介: 从C语言到C++⑨(第三章_C&C++内存管理)详解new和delete+面试题笔试题

从C语言到C++⑨(第三章_C&C++内存管理)详解new和delete+面试题笔试题(中):https://developer.aliyun.com/article/1513662

5. 定位new(了解)

5.1 定位new表达式(placement-new)

定位 new 表达式是在已分配的原始空间中调用构造函数初始化一个对象。

简单来说就是,定位new表达式可以在已有的空间进行初始化。

写法:

new(目标地址指针)类型                         // 不带参
new(目标地址指针)类型(该类型的初始化列表)       // 带参

注意:目标地址必须是一个指针。


5.2 定位new的使用场景

       定位 new 在特定情况下是有用的。

比如开的空间是从内存池来的,如果想初始化,我们就可以使用它。

因为内存池分配出的内存初始化,所以如果是自定义类型的对象,

需要使用 new 定义的表达式进行显示调用构造函数进行初始化。

#include<iostream>
using namespace std;
 
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;
}

模拟一下 new 的行为:

int main()
{
  A* p1 = (A*)malloc(sizeof(A));
  new(p1)A(10);
 
    // 模拟一下new的行为
  A* p2 = new A(2); 
 
  // 等价于:
  A* p3 = (A*)operator new(sizeof(A));
  new(p3)A(3);
 
  return 0;
}

平常不会这么写,但是有时内存不一定是从堆来的,比如从内存池来的,定位 new 就有用了。


6. 常见面试题

6.1 malloc/freenew/delete的区别

malloc/free和new/delete的共同点是:都从堆上申请空间,都需要用户手动释放。

不同的地方是:

  • 1. malloc和free是函数,new和delete是操作符
  • 2. malloc申请的空间不会初始化,new可以初始化
  • 3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[ ]中指定对象个数即可
  • 4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型。
  • 5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  • 6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。

7. 笔试选择题

7.1 下面有关c++内存分配堆栈说法错误的是( )

A.对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制

B. 对于栈来讲,生长方向是向下的,也就是向着内存地址减小的方向;对于堆来讲,它的生长方向是向上的,是向着内存地址增加的方向增长


C.对于堆来讲,频繁的 new/delete 势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题


D.一般来讲在 32 位系统下,堆内存可以达到4G的空间,但是对于栈来讲,一般都是有一定的空间大小的

7.2 C++中关于堆和栈的说法,哪个是错误的( )

A.堆的大小仅受操作系统的限制,栈的大小一般较小

B.在堆上频繁的调用new/delete容易产生内存碎片,栈没有这个问题

C.堆和栈都可以静态分配

D.堆和栈都可以动态分配

7.3 c++中,类ClassA的构造函数和析构函数的执行次数分别为( )

ClassA *pclassa=new ClassA[5];
 
delete pclassa;

A.5,1

B.1,1

C.5,5

D.程序可能崩溃


7.4 函数参数使用的空间是在()中申请的,malloc或new是在()中申请空间的?()

A.堆,栈

B.栈,堆

C.栈, 栈

D.堆,堆

7.5 下面有关malloc和new,说法错误的是? ( )

A.new 是创建一个对象(先分配空间,再调构造函数初始化), malloc分配的是一块内存

B.new 初始化对象,调用对象的构造函数,对应的delete调用相应的析构函数,malloc仅仅分配内存,free仅仅回收内存

C.new和malloc都是保留字,不需要头文件支持

D.new和malloc都可用于申请动态内存,new是一个操作符,malloc是是一个函数

7.6 设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为? ( )

C c;
void main()
{
  A*pa=new A();
  B b;
  static D d;
  delete pa;
}

A.A B C D

B.A B D C

C.A C D B

D.A C B D


7.7 使用 char* p = new char[100]申请一段内存,然后使用delete p释放,有什么问题?( )

A.会有内存泄露

B.不会有内存泄露,但不建议用

C.编译就会报错,必须使用delete []p

D.编译没问题,运行会直接崩溃


7.8 以下代码中,A 的构造函数和析构函数分别执行了几次: ( )

A.1、1

B.10、10

C.1、10

D.10、1


7.9 变量所在哪个内存区域以及变量所占空间大小是多少?

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

1. 选择题:


 选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)


 globalVar在哪里?____  staticGlobalVar在哪里?____


 staticVar在哪里?____  localVar在哪里?____


 num1 在哪里?____


 


 char2在哪里?____   *char2在哪里?___


 pChar3在哪里?____   *pChar3在哪里?____


 ptr1在哪里?____    *ptr1在哪里?____


2. 填空题:


 sizeof(num1) = ____;  


 sizeof(char2) = ____;   strlen(char2) = ____;


 sizeof(pChar3) = ____;   strlen(pChar3) = ____;


 sizeof(ptr1) = ____;

答案及解析

7.1 D


A.栈区主要存在局部变量和函数参数,其空间的管理由编译器自动完成,无需手动控制,堆区是自己申请的空间,在不需 要时需要手动释放

B.栈区先定义的变量放到栈底,地址高,后定义的变量放到栈顶,地址低,因此是向下生长的,堆区则相反

C.频繁的申请空间和释放空间,容易造成内存碎片,甚至内存泄漏,栈区由于是自动管理,不存在此问题

D.32位系统下,最大的访问内存空间为4G,所以不可能把所有的内存空间当做堆内存使用,故错误


7.2 C


A.堆大小受限于操作系统,而栈空间一般有系统直接分配

B.频繁的申请空间和释放空间,容易造成内存碎片,甚至内存泄漏,栈区由于是自动管理,不存在此问题

C.堆无法静态分配,只能动态分配

D.栈可以通过函数_alloca进行动态分配,不过注意,所分配空间不能通过free或delete进行释放


7.3 D


申请对象数组,会调用构造函数5次,delete由于没有使用[],此时只会调用一次析构函数,但往往会引发程序崩溃,要想完整释放数组空间,需要使用[]


7.4 B


7.5 C


A.new会申请空间,同时调用构造函数初始化对象,malloc只做一件事就是申请空间

B.new/delete与malloc/free最大区别就在于是否会调用构造函数与析构函数

C.需要头文件malloc.h,只是平时这个头文件已经被其他头文件所包含了,用的时候很少单独引入,故错误

D.new是操作符,malloc是函数


7.6 B


首先手动释放pa, 所以会先调用A的析构函数,其次C B D的构造顺序为 C D B,因为先构造全局对象,再构造局部静态对象,最后才构造普通对象,然而析构对象的顺序是完全按照构造的相反顺序进行的,所以答案为 B


7.7 B


A.对于内置类型,此时delete就相当于free,因此不会造成内存泄漏

B.正确

C.编译不会报错,建议针对数组释放使用delete[],如果是自定义类型,不使用方括号就会运行时错误

D.对于内置类型,程序不会崩溃,但不建议这样使用


7.8 B


A.申请数组空间,构造函数调用的次数就是数组的大小

B.正确

C.申请数组空间,构造函数调用的次数就是数组的大小

D.如果释放数组空间,delete使用了[],则会对应的调用数组大小次数的析构函数

7.9 本篇最上面已经说过了:

本篇完。

目录
相关文章
|
19天前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
30 3
|
4天前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
13 3
|
20天前
|
编译器 程序员 C语言
深入C语言:动态内存管理魔法
深入C语言:动态内存管理魔法
|
25天前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
6天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
9 0
|
15天前
|
存储 Kubernetes 架构师
阿里面试:JVM 锁内存 是怎么变化的? JVM 锁的膨胀过程 ?
尼恩,一位经验丰富的40岁老架构师,通过其读者交流群分享了一系列关于JVM锁的深度解析,包括偏向锁、轻量级锁、自旋锁和重量级锁的概念、内存结构变化及锁膨胀流程。这些内容不仅帮助群内的小伙伴们顺利通过了多家一线互联网企业的面试,还整理成了《尼恩Java面试宝典》等技术资料,助力更多开发者提升技术水平,实现职业逆袭。尼恩强调,掌握这些核心知识点不仅能提高面试成功率,还能在实际工作中更好地应对高并发场景下的性能优化问题。
|
15天前
|
C语言
保姆级教学 - C语言 之 动态内存管理
保姆级教学 - C语言 之 动态内存管理
13 0
|
20天前
|
存储 C语言
深入C语言内存:数据在内存中的存储
深入C语言内存:数据在内存中的存储
|
20天前
|
C语言 C++
c语言回顾-内存操作函数
c语言回顾-内存操作函数
38 0
|
22天前
|
存储 C语言 C++
来不及哀悼了,接下来上场的是C语言内存函数memcpy,memmove,memset,memcmp
本文详细介绍了C语言中的四个内存操作函数:memcpy用于无重叠复制,memmove处理重叠内存,memset用于填充特定值,memcmp用于内存区域比较。通过实例展示了它们的用法和注意事项。
54 0