从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 本篇最上面已经说过了:

本篇完。

目录
相关文章
|
23天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
52 6
|
1月前
|
存储 C语言 开发者
C 语言指针与内存管理
C语言中的指针与内存管理是编程的核心概念。指针用于存储变量的内存地址,实现数据的间接访问和操作;内存管理涉及动态分配(如malloc、free函数)和释放内存,确保程序高效运行并避免内存泄漏。掌握这两者对于编写高质量的C语言程序至关重要。
56 11
|
26天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
57 1
|
1月前
|
存储 缓存 C语言
【c++】动态内存管理
本文介绍了C++中动态内存管理的新方式——`new`和`delete`操作符,详细探讨了它们的使用方法及与C语言中`malloc`/`free`的区别。文章首先回顾了C语言中的动态内存管理,接着通过代码实例展示了`new`和`delete`的基本用法,包括对内置类型和自定义类型的动态内存分配与释放。此外,文章还深入解析了`operator new`和`operator delete`的底层实现,以及定位new表达式的应用,最后总结了`malloc`/`free`与`new`/`delete`的主要差异。
53 3
|
1月前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
52 3
|
2月前
|
存储 C语言 C++
【C++打怪之路Lv6】-- 内存管理
【C++打怪之路Lv6】-- 内存管理
53 0
【C++打怪之路Lv6】-- 内存管理
|
1月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
271 1
|
20天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
29天前
|
Java
JVM内存参数
-Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80
|
1月前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
22 3