首页 > 系统相关 >C++面试八股文:如何在堆上和栈上分配一块内存?

C++面试八股文:如何在堆上和栈上分配一块内存?

时间:2023-06-05 22:23:49浏览次数:47  
标签:面试官 八股文 int C++ 内存 new Foo delete

某日二师兄参加XXX科技公司的C++工程师开发岗位6面:

面试官: 如何在堆上申请一块内存?

二师兄:常用的方法有malloc,new等。

面试官:两者有什么区别?

二师兄:malloc是向操作系统申请一块内存,这块内存没有经过初始化,通常需要使用memset手动初始化。而new一般伴随三个动作,向操作系统申请一块内存,并执行类型的默认构造函数,然后返回类的指针。

面试官:嗯,那你知道calloc和realloc吗?

二师兄:calloc比malloc多做了一步,就是把申请的内存初始化成0。而realloc则可以改变当前指针所指向的内存块的大小。

面试官:好的。那么你知道这些api/操作符失败会发生什么吗?

二师兄:malloc/calloc/realloc失败会返回NULL,而new失败则会抛出异常。

面试官:有没有让new失败不抛出异常的方法?

二师兄:好像有,但是我不记得了。。。

面试官:没关系。。。我们都知道new和delete成对出现,new[]和delete[]也是成对出现,那么我想问,如果使用new[]创建的对象用delete释放了会发生什么?为什么?

二师兄:额。。。内存泄漏?对,会发生内存泄漏。因为内存没有被释放。

面试官:好的。我们都知道C++中的内存管理是一个比较麻烦的事情,现在有个需求,需要在程序中记录主动申请的内存和主动释放的内存,以确保没有发生内存泄漏。有什么好的方法吗?

二师兄:可以重载new和delete运算符。

面试官:如何重载new和delete运算符?

二师兄:我得查一下资料,这个重载用的很少。。。

面试官:(笑)好吧,最后一个问题,咱们上面一直在讨论堆中的内存的分配和释放,请问一下,如果在栈上分配一块固定的内存?栈中的内存如何释放?

二师兄:额。。。(思考)使用 char[size] ? 应该不需要手动释放。

面试官:好的,回去等通知吧。

对于二师兄的表现,小伙伴们能给打几分呢?我们先看看二师兄在面试中表现不太好的地方:

面试官:有没有让new失败不抛出异常的方法?

在C++中我们可以使用以下方法使得new运算符不抛出异常,

int* p = new (std::nothrow) int(42);
if(p == nullptr)
{
    //分配失败
}

这个特性需要C++11支持。

再看下一个问题:

如果使用new[]创建的对象用delete释放了会发生什么?

一定会发生内存泄漏吗?答案是,不一定。这取决于类型T。我们先看第一种情况:

class Foo
{
public:
    Foo():num_(42){}
private:
    int num_;
};

Foo* pf = new Foo[1024];
delete pf;

当类型T没有管理资源时,delete pf会把整个申请的1024个Foo所占用的内存全部归还给操作系统,此时并没有内存泄漏。再看下一种情况:

class Foo
{
public:
    Foo():num_(new int(42)){}
    ~Foo(){delete num_;}
private:
    int* num_;
};

Foo* pf = new Foo[1024];
delete pf; 

此时会造成内存泄漏,原因很简单。在执行delete[]时,首先逆序执行每个元素的析构函数,然后再把整块内存归还给操作系统。而delete只会把内存还给操作系统,没有执行析构函数。当类没有资源需要管理时,执行与不执行析构函数都无关紧要,但是当类中需要管理资源时,析构函数的执行就至关重要了。

如何重载new和delete运算符?

#include <iostream>
#include <cstdlib>
#include <map>
struct MemoryInfo {
    size_t size;
    const char* file;
    int line;
};

std::map<void*, MemoryInfo> memoryMap;

void* operator new(size_t size, const char* file, int line) {
    void* ptr = std::malloc(size);
    memoryMap[ptr] = {size, file, line};
    return ptr;
}

void operator delete(void* ptr) noexcept {
    auto it = memoryMap.find(ptr);
    if (it != memoryMap.end()) {
        std::free(ptr);
        memoryMap.erase(it);
    }
}

#define new new(__FILE__, __LINE__)

int main() {
    int* p = new int(42);

    for (const auto& [ptr, info] : memoryMap) {
        std::cout << "Memory allocated at " << ptr << " with size " << info.size
                  << " in file " << info.file << " at line " << info.line << std::endl;
    }
    
    delete p;
    
    for (const auto& [ptr, info] : memoryMap) {
        std::cout << "Memory allocated at " << ptr << " with size " << info.size
                  << " in file " << info.file << " at line " << info.line << std::endl;
    }
    return 0;
}

最后一个问题:

如果在栈上分配一块固定的内存?栈中的内存如何释放?

使用alloca,虽然简单,但是很多人可能都没有接触过:

int* p = (int*)alloca(4);
*p = 42;

栈上申请的内存不需要手动释放。注意,如果栈溢出,alloca的行为时未定义的。

好了,今日份面试到这里就结束了,小伙伴们,对于今天二师兄的面试,能打几分呢?

关注我,带你21天“精通”C++!(狗头)

标签:面试官,八股文,int,C++,内存,new,Foo,delete
From: https://www.cnblogs.com/binarch/p/17459075.html

相关文章

  • win10,vs2015深度学习目标检测YOLOV5+deepsort C++多目标跟踪代码实现,源码注释,拿来即
    int8,FP16等选择,而且拿来即用,自己再win10安装上驱动可以立即使用,不用在自己配置,支持答疑。自己辛苦整理的,求大佬打赏一顿饭钱。苦苦苦、平时比较比忙,自己后期会继续发布真实场景项目;欢迎下载。优点:1、架构清晰,yolov5和sort是分开单独写的,可以随意拆解拼接,都是对外接口。2、支持答疑......
  • c++
    因此,想要查看一个数组变量的地址,代码为:intarr[10];cout<<arr<<endl;//注意,arr之前无需&。查看数组中第一个元素的地址:intarr[10];cout<<&arr[0]<<endl;递归:递归的过程是压栈的过程,递归结束,会出栈进行计算二维数组:二维数组的存储是按照行进行扫描,每行的元素依......
  • C++设计模式:观察者模式
    观察者模式又叫做发布-订阅模式;观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动的更新自己。有两大类(主题和观察者)一共四个角色从上面我们可以看到,这里面包含了:(1)抽象主题/抽象......
  • 初遇C++(一)
    认识C++C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机,20世纪80年代,计算机界提出了OOP(objectorientedprogramming:面向对象)思想,支持面向对象的程序设计语言应运而生。1982年,本贾......
  • c++ condition_variable wait unique_lock,cv.notifyall()
    #include<atomic>#include<chrono>#include<cmath>#include<condition_variable>#include<cstddef>#include<forward_list>#include<fstream>#include<functional>#include<future>#include<iom......
  • C++11中的std::function
    看看这段代码先来看看下面这两行代码:std::function<void(EventKeyboard::KeyCode,Event*)>onKeyPressed;std::function<void(EventKeyboard::KeyCode,Event*)>onKeyReleased;这两行代码是从Cocos2d-x中摘出来的,重点是这两行代码的定义啊。std::function这是什么东西?如果你对上......
  • C、C++、Java等控制汽车- 汽车引擎控制系统
    汽车代码的实现 - 汽车引擎控制系统汽车引擎控制系统是汽车代码中最重要的一部分之一。控制系统的目的是确保引擎在各种负载下运行顺畅,并最大程度地减少排放。控制系统的主要组成部分是ECU(电子控制单元),它是一种具有微处理器的电子设备,可接收传感器信号并控制汽车的各种功能,从燃......
  • 汽车代码的实现 - 用python、c++写的自动驾驶系统
    汽车代码的实现-自动驾驶系统自动驾驶系统是近年来发展迅速的汽车代码领域之一。这个系统旨在使用传感器和计算机,自动化控制车辆行驶,消除驾驶员的错误和危险。实现自动驾驶系统需要使用一些重要的编程语言和技术,如Python、Matlab、深度学习和机器视觉算法。此外,还需要使用各种......
  • C++设计模式:单例模式
    什么是单例模式?单例模式指在整个系统生命周期里,保证一个类只能产生一个实例,确保该类的唯一性。单例模式分类有哪些?单例模式可以分为懒汉式和饿汉式,两者之间的区别在于创建实例的时间不同:懒汉式:指系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。(这种方式要考......
  • C++ 子类调用父类的方法,静态方法的调用
    #include<iostream>classA{public:A();~A();virtualvoidsay(){std::cout<<"HellothisisclassA!\n";}staticvoidtest(){std::cout<<"HellothisisclassAtestfunction..!\n";}private:};......