首页 > 编程语言 >【C++】new的底层实现原理

【C++】new的底层实现原理

时间:2024-09-11 22:22:08浏览次数:8  
标签:malloc C++ 内存 new 构造函数 底层 delete

文章目录

理解 C++ new 的原理

在 C++ 中,new 操作符用于在堆上动态分配内存,它是开发者管理内存的核心工具之一。相比于 C 语言中的 mallocnew 不仅仅负责分配内存,还会调用对象的构造函数,确保对象的正确初始化。本文将深入探讨 new 的工作原理,并解释它在内存管理中的重要角色。

1. new 的基本工作流程

当我们在 C++ 中使用 new 时,通常会执行以下几步操作:

int* p = new int(5);

这一行代码的背后,new 的执行流程如下:

  • 内存分配new 首先会调用低级别的内存分配函数,类似于 malloc,从堆中为对象分配足够的内存。
  • 调用构造函数:分配内存后,new 会调用对象的构造函数,将对象初始化。在上述例子中,newint 分配内存并将其值设为 5。
  • 返回指针:最终,new 返回指向该内存区域的指针,使程序能够使用这块内存。
2. newmalloc 的区别

尽管 newmalloc 都能分配内存,但它们在行为上存在以下几个关键区别:

  • 调用构造函数new 会自动调用对象的构造函数,而 malloc 仅仅分配内存,不会初始化对象。使用 malloc 分配的内存区域是未初始化的,需要手动调用构造函数来初始化。

  • 类型安全new 返回的是正确类型的指针,而 malloc 返回 void*,因此在 C++ 中需要显式地进行类型转换。

  • 失败处理:当内存分配失败时,new 会抛出 std::bad_alloc 异常,而 malloc 则会返回 NULL。这使得 new 更加符合 C++ 的异常处理机制。

3. new 的底层实现

在大多数 C++ 实现中,new 的底层机制可以分为两个部分:分配器构造器。C++ 标准库允许重载 new 操作符,因此开发者可以自定义内存分配的方式。

分配器(Allocator) 负责从堆中分配内存,典型的实现会调用低级的内存管理函数,比如 malloc 或者操作系统提供的分配函数。

void* operator new(size_t size) {
    if (void* ptr = malloc(size)) {
        return ptr;
    } else {
        throw std::bad_alloc();
    }
}

构造器(Constructor) 是在内存分配完成后调用的,用来初始化对象。如果是基本类型(例如 intchar),则直接存储值;如果是用户自定义类型,则调用相应的构造函数。

void* memory = operator new(sizeof(MyClass));  // 分配内存
MyClass* obj = new(memory) MyClass();          // 调用构造函数
4. new[]delete 的配对使用

C++ 中还有一个用于分配数组的操作符:new[]。它与单个对象的 new 类似,但会为多个对象分配连续的内存空间,并依次调用构造函数。

int* arr = new int[10];  // 分配 10 个整型的数组

使用 new[] 分配的内存,必须使用 delete[] 来释放,否则会造成内存泄漏。

delete[] arr;

相反,对于单个对象使用 new 分配的内存,应该使用 delete 来释放。

int* p = new int(5);
delete p;

如果错误地将 delete[] 用于 new 分配的内存,或者反之,将 delete 用于 new[] 分配的内存,可能导致未定义行为。

5. 自定义 newdelete

C++ 提供了重载 newdelete 的能力,允许开发者为特定的类自定义内存管理策略。通常用于优化内存分配效率或者进行调试。

void* operator new(size_t size) {
    std::cout << "Custom new for size: " << size << std::endl;
    return malloc(size);
}

void operator delete(void* ptr) noexcept {
    std::cout << "Custom delete" << std::endl;
    free(ptr);
}

重载 newdelete 可以帮助开发者监控内存使用情况,或者为特定的对象池(memory pool)提供优化的分配方式。

6. 定位new

C++ 中的 定位new 是一种特殊的 new 形式,它允许在已经分配的内存上构造对象,而不会重新分配内存。

char buffer[sizeof(MyClass)];
MyClass* obj = new(buffer) MyClass();

placement new 主要用于性能优化,比如在内存池中直接复用内存。它不会调用 operator new,因此也不负责分配内存。

7. 内存泄漏与异常安全
  • 内存泄漏:如果使用 new 分配的内存没有通过 delete 释放,则会导致内存泄漏。因此,建议在可能出现异常的情况下使用智能指针(如 std::unique_ptrstd::shared_ptr),以确保内存能够自动释放。

  • 异常安全:当 new 分配内存后,如果构造函数抛出异常,那么 C++ 运行时会自动释放已经分配的内存,确保不会发生内存泄漏。

new 是 C++ 中内存管理的重要工具,负责内存分配和对象初始化。它与 malloc 相比,具有更高的类型安全性和异常处理机制。在理解了 new 的底层原理之后,开发者可以通过自定义 newdelete 来优化内存分配,并在需要时使用 placement new 进行高级内存管理。

标签:malloc,C++,内存,new,构造函数,底层,delete
From: https://blog.csdn.net/2403_86785171/article/details/142151828

相关文章

  • 【C++】C++ 多态的底层实现原理
    文章目录1.多态的定义与作用2.虚函数与虚函数表(vtable)3.虚函数表(vtable)4.虚函数调用的底层过程5.内存布局中的虚函数指针6.多重继承中的虚函数表7.RTTI与动态类型识别1.多态的定义与作用多态指的是同一操作在不同对象上具有不同的表现。C++中多态分为两......
  • 递归(c++)
    递归1、斐波那契额数列基础代码#include<iostream>usingnamespacestd;intf(intn){ if(n==1)return1; if(n==2)return2; returnf(n-1)+f(n-2);}intmain(){ intn; cin>>n; cout<<f(n)<<endl; return0;}递归实现指数......
  • c++ 数字转化成 string
       ULONG转换成string方法1:使用std::to_string(C++11及更高版本)std::to_string是将数字转换为字符串的简单方式,适用于C++11及更高版本。#include<iostream>#include<string>intmain(){ULONGvalue=1234567890UL;//定义一个ULONG类型的值/......
  • c++ string 转换成 guid
      在C++中,将一个字符串转换为GUID(GloballyUniqueIdentifier)可以通过以下方法实现。GUID通常是128位(16字节)的标识符,以标准格式表示,例如:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx。在C++中,常用的库之一是WindowsAPI,它提供了处理GUID的相关功能。这里是一个示例代码,将字符串转换......
  • 【自用22.】C++类的静态数据成员以及静态成员函数
    需要获取总的人数,如何实现?方案一:使用全局变量,在构造函数中对这个全局变量进行修改具体代码如下:在Human.cpp中定义全局变量,并在构造函数中对人数进行增加操作#include"Human.h"#include<iostream>usingnamespacestd;intHumanCount=0;Human::Human(){ name......
  • C++复习day10
    智能指针为什么需要智能指针?#include<iostream>usingnamespacestd;intdiv(){ inta,b; cin>>a>>b; if(b==0) throwinvalid_argument("除0错误"); returna/b;}voidFunc(){ //1、如果p1这里new抛异常会如何? //2、如果p2这里new抛异常会......
  • C++ web框架:matt-42/lithium
    一、代码示例#include<lithium_http_server.hh>#include<lithium_pgsql.hh>#include"symbols.hh"usingnamespaceli;intmain(){//创建PostgreSQL数据库连接pgsql_databasedb=pgsql_database(s::host="localhost"......
  • C++中的数组,字符串数组,pair数组
    1.C++中的字符串数组: 2.C++中的常量数组 这个constpair<int,string>valueSymbols[]定义了一个常量数组,数组中的每个元素都是一个pair<int,string>类型的对象。pair是C++标准模板库(STL)中的一个模板类,用于将两个值组合成一个单一的对象。在这个特定的例子中,pair的第一个......
  • C++入门教程:第八篇 - 文件I/O操作
    C++入门教程:第八篇-文件I/O操作文件I/O(输入/输出)是程序与外部存储设备进行数据交换的关键操作。在C++中,文件I/O操作由标准库提供的流类完成。通过这些流类,程序可以读写文件,处理文件内容。本文将介绍C++中的文件I/O基础,包括如何打开、读写和关闭文件。1.文件流基础C++提......
  • C++ 虚析构函数简单测试
    classBase{public:virtual~Base(){cout<<"~Base"<<'\n';}};classDerived:publicBase{public:~Derived(){cout<<"~Derived"<<'\n';}};intmain(){{......