C++中动态内存管理的实现

 更新时间:2025年09月08日 15:47:33   作者:祁同伟.  
本文主要介绍了C++中动态内存管理的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一. C&C++内存分布

  1. 栈:非静态局部变量/函数参数/返回值等,栈是向下增长的。
  2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(Linux详细讲解)
  3. 堆:程序运行时动态内存分配,堆是可以上增长的。
  4. 数据段、静态区:存储全局数据和静态数据。
  5. 代码段、常量区:可执行的代码/只读常量。
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
    static int staticVar = 1;
    int localVar = 1;
    int num1[10] = { 1,2,3,4 };
}

globalVar、staticGlobalVar、staticVar在静态区
localVar、num1在栈

void Test
{
    char char2[] = "abcd";
    const char* pChar3 = "abcd"; // 常量字符串,如果不加const则权限放大,报错
    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);
}

char2、*char2、pChar3、ptr1在栈
*pChar3在常量区
*ptr1在堆

二. C++内存管理方式

C主要靠malloc/calloc/realloc/free等函数手段完成
C++通过 new delete 操作符进行动态内存管理

1. 内置类型

内置类型C&C++没太大区别。
C++的 new delete 不用算多少字节、不用强转

new、delete 申请和释放的是单个元素的空间
new[ ]、delete[ ] 申请的是连续空间

int main()
{
    // C
    int* p1 = (int*)malloc(sizeof(int)); // 随机值
    free(p1);

    // C++ 动态申请一个int类型的空间
    int* p2 = new int; // 随机值
    delete p2;

    // C
    int* p3 = (int*)malloc(sizeof(int)*10); // 全是随机值
    free(p3);

    // C++ 动态申请10个int类型的空间
    int* p4 = new int[10]; // 全是随机值
    delete[] p4;

    // C++ 动态申请一个int类型的空间并初始化为10
    int* p5 = new int(10);
    delete p5;

    int* p6 = new int[10]{1,2,3}; // 其余是0
    delete[] p6;

    return 0;
}

2. 自定义类型

C:malloc 只是单纯开空间,不初始化;free 不清理资源
C++:new 出来就调用构造函数初始化;delete 调用析构函数

struct ListNode
{
    int _val;
    struct ListNode* _next;

    ListNode(int x) // 构造函数
        :_val(x)
        , _next(NULL)
    {}
};

struct ListNode* BuyListNode(int x)
{
    // 单纯开空间
    struct ListNode* newnode = (struct ListNode*)malloc(sizeof(struct ListNode));
    // 检查
    newnode->_next = NULL;
    newnode->_val = x;

    return newnode;
}

int main()
{
    struct ListNode* n1 = BuyListNode(1);
    struct ListNode* n2 = BuyListNode(2);
    struct ListNode* n3 = BuyListNode(3);

    // 开空间+调用构造函数初始化
    ListNode* nn1 = new ListNode(1);
    ListNode* nn2 = new ListNode(2);
    ListNode* nn3 = new ListNode(3);

    return 0;
}
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 = (A*)malloc(sizeof(A));
	A* p2 = new A(1);
	free(p1);
	delete p2;

	A* p5 = (A*)malloc(sizeof(A) * 10);
	A* p6 = new A[4];
	free(p5);
	delete[] p6;

	return 0;
}

p6 全初始化成 0,因为构造函数给了缺省参数

如果没有默认构造函数

class A
{
public:
    A(int a)
        : _a(a)
    {
        cout << "A():" << this << endl;
    }

    ~A()
    {
        cout << "~A():" << this << endl;
    }

private:
    int _a;
};

int main()
{
	// A* p6 = new A[4]; // 报错:“A”: 没有合适的默认构造函数可用
    A* p6 = new A[4]{ 1,2,3,4 };
    A* p6 = new A[4]{ A(1),A(2),A(3),A(4) }; // 给4个匿名对象
	delete[] p6;
	return 0;
}

如果多个参数

class A
{
public:
    A(int a, int b)
        : _a(a)
    {
        cout << "A():" << this << endl;
    }

    A(const A& aa)
        : _a(aa._a)
    {
        cout << "A(const A& aa):" << this << endl;
    }

    ~A()
    {
        cout << "~A():" << this << endl;
    }

private:
    int _a;
};

int main()
{
    A* p2 = new A(1, 1);
	delete p2;

    A* p6 = new A[4]{ A(1,1),A(2,2),A(3,3),A(4,4) };
	delete[] p6;

	return 0;
}

构造+拷贝构造-->优化为直接构造

此时,没有默认构造,必须传4个匿名对象。如果给了缺省参数,有了默认构造,可以少传

三. new delete 底层

operator new、operator delete 函数:

他们不是运算符重载,是库里的全局函数。他们不是给程序员用的,是给 new、delete用的

C语言处理失败,返回错误码。C语言喜欢用 malloc,申请失败返回 0(NULL)
C++处理失败,抛异常

C++源代码中:
operator new 实际也是通过malloc来申请空间。若malloc申请空间成功就直接返回;否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常
operator delete 最终是通过free来释放空间的(malloc、free只是隐藏起来了,不是没用了)

new[ ]:
        1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
        2. 在申请的空间上执行N次构造函数

delete[ ]:
        1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
        2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

用 Stack 理解

int main()
{
	// 申请一个堆上的栈对象
	Stack* p1 = new Stack;
	delete p1;

	return 0;
}

用 new 就不用加 if (NULL==_array) 这种检查了。new 失败抛异常,我们在所有 new 的地方 try catch捕获就行

int main()
{
	try
	{
		// 申请一个堆上的栈对象
		Stack* p1 = new Stack;
		delete p1;
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}

	return 0;
}

这里的 try catch 不仅能捕获 main 函数里的;还能捕获构造函数里面的异常
只要是在范围内,直接、间接调用的都可以捕获

四. 定位 new 表达式(placement-new)

new 的其他操作

定位new表达式是对已有空间调用构造函数 初始化一个对象

格式:new (指针) 类型new (指针) 类型(类型的初始化列表)

int main()
{
    // p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
    A* p1 = (A*)malloc(sizeof(A));
    // 显示调用构造函数
    new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参 new(p1)A(1);

    // 显示调用析构函数
    p1->~A();
    free(p1);
    // 自定义类型的对象才能自动调用构造、析构函数。p1是内置类型,必须显示调用析构

    return 0;
}

应用场景:池化技术:内存池、线程池、连接池

定位new表达式 在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化

需要频繁申请和释放内存。new 是直接找堆,路径长、麻烦
建池子,提前申请一大块内存放到池子里。要的时候直接找池子要,池子没了再去找堆。效率高

五. 内存泄露

https://www.jb51.net/program/334321hr3.htm

到此这篇关于C++中动态内存管理的实现的文章就介绍到这了,更多相关C++ 动态内存管理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解析如何用指针实现整型数据的加法

    解析如何用指针实现整型数据的加法

    本篇文章是对用指针实现整型数据加法的方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • c++实现md5加密的代码

    c++实现md5加密的代码

    这篇文章主要介绍了c++实现md5加密的实例代码,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • C和C++混合编程问题

    C和C++混合编程问题

    这篇文章主要介绍了C和C++混合编程问题,需要的朋友可以参考下
    2015-10-10
  • C++实现聊天小程序

    C++实现聊天小程序

    这篇文章主要为大家详细介绍了C++实现聊天小程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • 解析ActiveMQ的使用说明总结

    解析ActiveMQ的使用说明总结

    本篇文章是对ActiveMQ的使用进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • 基于C语言实现五子棋游戏

    基于C语言实现五子棋游戏

    这篇文章主要为大家详细介绍了基于C语言实现五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • 基于C++实现TCP聊天室功能

    基于C++实现TCP聊天室功能

    这篇文章主要为大家详细介绍了基于C++实现TCP聊天室功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • 深入C++可见性与生命期的区别详解

    深入C++可见性与生命期的区别详解

    本篇文章对C++中可见性与生命期的区别进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • 实现Dijkstra算法最短路径问题详解

    实现Dijkstra算法最短路径问题详解

    这篇文章主要介绍了实现Dijkstra算法最短路径问题详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • C语言枚举(enum)和联合(union)实例分享

    C语言枚举(enum)和联合(union)实例分享

    在本篇文章里小编给大家整理了关于C语言枚举(enum)和联合(union)实例内容,需要的朋友们可以学习下。
    2020-03-03

最新评论