您的位置:首页 > 文旅 > 旅游 > 椋鸟C++笔记#5:C++内存管理

椋鸟C++笔记#5:C++内存管理

2024/12/23 10:49:12 来源:https://blog.csdn.net/StarlingLin/article/details/139585382  浏览:    关键词:椋鸟C++笔记#5:C++内存管理

文章目录

      • C语言中的动态内存管理
      • C\+\+中的动态内存管理
        • 使用new/delete操作内置类型
        • 使用new/delete操作自定义类型
        • operator new(operator new[])与operator delete(operator delete[])函数
          • operator new函数
          • operator delete函数
          • operator new[]和operator delete[]
        • new与delete的实现原理
        • 定位new表达式(placement-new)
      • 检测内存泄露

萌新的学习笔记,写错了恳请斧正。

C语言中的动态内存管理

C语言中,我们使用malloccallocreallocfree来动态管理内存。

int main()
{int* p = (int*)malloc(sizeof(int));	//动态开辟free(p);int* q = (int*)calloc(4, sizeof(int));	//动态开辟并赋值int* r = (int*)realloc(q, 10 * sizeof(int));	//重新分配空间free(r);
}

C++中的动态内存管理

C++中有更简单方便的内存管理方式,那就是使用newdelete来管理内存。

使用new/delete操作内置类型

注意:如果使用new开辟空间就用delete删除,如果使用new[]开辟数组就用delete[]删除。

  1. 动态申请与删除一个内置类型变量的空间:

    int* pi = new int;
    int* pf = new float;
    int* pl = new long;
    int* pb = new bool;delete pi;
    delete pf;
    delete pl;
    delete pb;
    
  2. 动态申请与删除一个内置类型变量的空间并完成初始化:

    int* pi = new int(114514);
    int* pf = new float(3.14f);
    int* pl = new long(1145141919810);
    int* pb = new bool(true);delete pi;
    delete pf;
    delete pl;
    delete pb;
    
  3. 动态申请与删除内置类型变量数组的空间:

    int* pi = new int[5];
    int* pf = new float[5];
    int* pl = new long[5];
    int* pb = new bool[5];delete[] pi;
    delete[] pf;
    delete[] pl;
    delete[] pb;
    
  4. 动态申请与删除内置类型变量数组的空间并完成初始化:

    int* pi = new int[5]{1, 2, 3, 4, 5};
    float* pf = new float[5]{1.1f, 2.2f, 3.3f, 4.4f, 5.5f};
    long* pl = new long[5]{10, 20, 30, 40, 50};
    bool* pb = new bool[5]{true, false, true, false, true};delete[] pi;
    delete[] pf;
    delete[] pl;
    delete[] pb;
    
使用new/delete操作自定义类型

这与自定义类型区别主要在于:使用new/delete操作自定义类型时会自动调用自定义类型的构造/析构函数。

class A
{
public:A(int a = 0): m_a(a){cout << "A" << endl;}~A(){cout << "~A" << endl;}private:int m_a;
};int main()
{A* oA = new A;delete oA;A* oB = (A*)malloc(sizeof(A));free(oB);return 0;
}

该程序运行结果为:

A
~A
operator new(operator new[])与operator delete(operator delete[])函数

看到operator new和operator delete,很多人会以为这是new与delete的重载函数。

但是,operator new和operator delete不能理解为是new与delete的重载函数!

operator new与operator delete其实是系统提供的全局函数,而new和delete在底层其实就是调用了这两个函数来实现的。

以下给出的函数定义随编译器不同有所变化!

operator new函数
void* operator new(std::size_t size) 
{if (size == 0)	// 如果请求的大小为0,分配至少一个字节{size = 1;}while (true){void* p = std::malloc(size);  // 使用malloc分配内存if (p) {return p;  // 如果分配成功,返回指针}std::new_handler handler = std::get_new_handler();  // 获取当前的new_handlerif (!handler) {throw std::bad_alloc();  // 如果没有设置new_handler,抛出bad_alloc异常}handler();  // 调用new_handler}
}
operator delete函数
void operator delete(void* ptr) noexcept	//noexcept说明此函数不会抛出异常
{std::free(ptr);  // 使用free释放内存
}
operator new[]和operator delete[]

同样的,new[]和delete[]在底层调用的是operator new[]和operator delete[]函数。

void* operator new[](std::size_t size) 
{if (size == 0) {size = 1;}while (true) {void* p = std::malloc(size);if (p) {return p;}std::new_handler handler = std::get_new_handler();if (!handler) {throw std::bad_alloc();}handler();}
}void operator delete[](void* ptr) noexcept 
{std::free(ptr);
}
new与delete的实现原理

如果申请的是内置类型的空间,new和malloc、delete和free基本类似。

不同的是:

  1. new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间
  2. new在申请空间失败时会抛异常;malloc会返回NULL。

对于自定义类型:

  1. new的原理:
    • 调用 operator new 函数申请空间。
    • 执行构造函数。
  2. delete的原理:
    • 执行析构函数。
    • 调用 operator delete 函数释放空间。
  3. new[]的原理:
    • 调用 operator new[] 函数申请空间。
    • 分别执行构造函数。
    • 在这片空间前紧挨的位置申请4个字节用于保存N。
  4. delete[]的原理:
    • 往前读取4个字节,获得N。
    • N个元素分别执行析构函数。
    • 调用 operator delete[] 函数释放空间。
定位new表达式(placement-new)

我们可以直接通过指针显式的调用析构函数:

#include <iostream>using namespace std;class A
{
public:A(int a = 0): m_a(a){cout << "A()" << this << endl;}~A(){cout << "~A()" << this << endl;}private:int m_a;
};int main()
{A* p1 = new A;p1->~A();free(p1);return 0;
}

那我们能直接显式调用构造函数吗?答案是否定的。

但是我们可以使用定位new表达式

#include <iostream>using namespace std;class A
{
public:A(int a = 0): m_a(a){cout << "A()" << this << endl;}~A(){cout << "~A()" << this << endl;}private:int m_a;
};int main()
{A* p1 = (A*)malloc(sizeof(A));new (p1) A;	//在地址p1处执行类A的构造函数p1->~A();free(p1);return 0;
}

其结构就是new加上(这里放地址)加上类名

或者也可以同时初始化:

int main()
{A* p1 = (A*)malloc(sizeof(A));new (p1) A (10);	//在地址p1处执行类A的构造函数并初始化为10p1->~A();free(p1);return 0;
}

当然我们也能显示的调用operator new(operator new[])与operator delete(operator delete[])函数:

#include <iostream>using namespace std;class A
{
public:A(int a = 0): m_a(a){cout << "A()" << this << endl;}~A(){cout << "~A()" << this << endl;}private:int m_a;
};int main()
{A* p1 = (A*)malloc(sizeof(A));new (p1) A;p1->~A();free(p1);A* p2 = (A*)operator new(sizeof(A));new (p2) A(10);p2->~A();operator delete(p2);return 0;
}

检测内存泄露

在 Visual Stodio 中我们可以使用_CrtDumpMemoryLeaks()函数来检测内存泄露,如果发生内存泄露这个函数会在输出窗口给出提示。

#include <iostream>using namespace std;int main()
{int* p = new int;_CrtDumpMemoryLeaks();return 0;
}

执行这段代码,我们可以在来源为调试的输出看到如下内容:

Detected memory leaks!
Dumping objects ->
{81} normal block at 0x00000221287C7400, 4 bytes long.Data: <    > CD CD CD CD 
Object dump complete.

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com