C++解析-外传篇(3):动态内存申请的结果

0.目录

1.动态内存申请一定成功吗?

2.new_handler() 函数

3.小结

1.动态内存申请一定成功吗?

问题:

动态内存申请一定成功吗?

常见的动态内存分配代码:

C代码:

C++代码:

必须知道的事实!

  • malloc函数申请失败时返回NULL值
  • new关键字申请失败时(根据编译器的不同)
    1. 返回NULL值
    2. 抛出 std::bad_alloc 异常

问题:

new语句中的异常是怎么抛出来的?

new关键字在C++规范中的标准行为:

  • 在堆空间申请足够大的内存

    1. 成功:

      1. 在获取的空间中调用构造函数创建对象
      2. 返回对象的地址
    2. 失败:
      1. 抛出 std::bad_alloc 异常
  • new在分配内存时
    1. 如果空间不足,会调用全局的 new_handler() 函数
    2. new_handler() 函数中抛出 std::bad_alloc 异常
  • 可以自定义 new_handler() 函数
    1. 处理默认的new内存分配失败的情况

2.new_handler() 函数

new_handler() 的定义和使用:

问题:

如何跨编译器统一 new 的行为,提高代码移植性?

解决方案:

  • 全局范围(不推荐)

    1. 重新定义 new / delete 的实现,不抛出任何异常
    2. 自定义 new_handler() 函数,不抛出任何异常
  • 类层次范围
    1. 重载 new / delete,不抛出任何异常
  • 单次动态内存分配
    1. 使用 nothrow 参数,指明 new 不抛出异常

示例1——证明存在 new_handler() 函数:

#include <iostream>
#include <cstdlib>

using namespace std;

class Test
{
    int m_value;
public:
    Test()
    {
        cout << "Test()" << endl;

        m_value = 0;
    }

    ~Test()
    {
        cout << "~Test()" << endl;
    }

    void* operator new (unsigned long size)
    {
        cout << "operator new: " << size << endl;

        // return malloc(size);

        return NULL;
    }

    void operator delete (void* p)
    {
        cout << "operator delete: " << p << endl;

        free(p);
    }

    void* operator new[] (unsigned long size)
    {
        cout << "operator new[]: " << size << endl;

        return malloc(size);
    }

    void operator delete[] (void* p)
    {
        cout << "operator delete[]: " << p << endl;

        free(p);
    }
};

void my_new_handler()
{
    cout << "void my_new_handler()" << endl;
}

void ex_func_1()
{
    new_handler func = set_new_handler(my_new_handler);

    try
    {
        cout << "func = " << func << endl;

        if( func )
        {
            func();
        }
    }
    catch(const bad_alloc&)
    {
        cout << "catch(const bad_alloc&)" << endl;
    }
}

int main(int argc, char *argv[])
{
    ex_func_1();

    return 0;
}

运行结果为:

[[email protected] Desktop]# g++ test.cpp
test.cpp: In static member function ‘static void* Test::operator new(long unsigned int)’:
test.cpp:28: warning: ‘operator new’ must not return NULL unless it is declared ‘throw()’ (or -fcheck-new is in effect)
[[email protected] Desktop]# ./a.out
func = 0

(在g++编译器中没有设置这个全局的 new_handler() 函数,bcc编译器中实现了这个全局的 new_handler() 函数。)

示例2——动态内存申请失败的结果:

#include <iostream>
#include <cstdlib>

using namespace std;

class Test
{
    int m_value;
public:
    Test()
    {
        cout << "Test()" << endl;

        m_value = 0;
    }

    ~Test()
    {
        cout << "~Test()" << endl;
    }

    void* operator new (unsigned long size)
    {
        cout << "operator new: " << size << endl;

        // return malloc(size);

        return NULL;
    }

    void operator delete (void* p)
    {
        cout << "operator delete: " << p << endl;

        free(p);
    }

    void* operator new[] (unsigned long size)
    {
        cout << "operator new[]: " << size << endl;

        return malloc(size);
    }

    void operator delete[] (void* p)
    {
        cout << "operator delete[]: " << p << endl;

        free(p);
    }
};

void ex_func_2()
{
    Test* pt = new Test();

    cout << "pt = " << pt << endl;

    delete pt;
}

int main(int argc, char *argv[])
{
    ex_func_2();

    return 0;
}

运行结果为:

[[email protected] Desktop]# g++ test.cpp
test.cpp: In static member function ‘static void* Test::operator new(long unsigned int)’:
test.cpp:28: warning: ‘operator new’ must not return NULL unless it is declared ‘throw()’ (or -fcheck-new is in effect)
[[email protected] Desktop]# ./a.out
operator new: 4
Test()
Segmentation fault (core dumped)

(g++编译器中报错:段错误。不同的编译器报错信息不同。)

示例3——统一不同编译器动态内存申请失败后的行为:

#include <iostream>
#include <cstdlib>

using namespace std;

class Test
{
    int m_value;
public:
    Test()
    {
        cout << "Test()" << endl;

        m_value = 0;
    }

    ~Test()
    {
        cout << "~Test()" << endl;
    }

    void* operator new (unsigned long size) throw()
    {
        cout << "operator new: " << size << endl;

        // return malloc(size);

        return NULL;
    }

    void operator delete (void* p)
    {
        cout << "operator delete: " << p << endl;

        free(p);
    }

    void* operator new[] (unsigned long size) throw()
    {
        cout << "operator new[]: " << size << endl;

        // return malloc(size);

        return NULL;
    }

    void operator delete[] (void* p)
    {
        cout << "operator delete[]: " << p << endl;

        free(p);
    }
};

void ex_func_2()
{
    Test* pt = new Test();

    cout << "pt = " << pt << endl;

    delete pt;

    pt = new Test[5];

    cout << "pt = " << pt << endl;

    delete[] pt;
}

int main(int argc, char *argv[])
{
    ex_func_2();

    return 0;
}

运行结果为:

[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out
operator new: 4
pt = 0
operator new[]: 28
pt = 0

显示的调用析构函数

示例4——让编译器申请失败后返回空指针而不是抛出异常:

#include <iostream>
#include <cstdlib>

using namespace std;

class Test
{
    int m_value;
public:
    Test()
    {
        cout << "Test()" << endl;

        m_value = 0;
    }

    ~Test()
    {
        cout << "~Test()" << endl;
    }

    void* operator new (unsigned long size) throw()
    {
        cout << "operator new: " << size << endl;

        // return malloc(size);

        return NULL;
    }

    void operator delete (void* p)
    {
        cout << "operator delete: " << p << endl;

        free(p);
    }

    void* operator new[] (unsigned long size) throw()
    {
        cout << "operator new[]: " << size << endl;

        // return malloc(size);

        return NULL;
    }

    void operator delete[] (void* p)
    {
        cout << "operator delete[]: " << p << endl;

        free(p);
    }
};

void ex_func_3()
{
    int* p = new(nothrow) int[10];

    // ... ...

    delete[] p; 

    int bb[2] = {0};

    struct ST
    {
        int x;
        int y;
    };

    ST* pt = new(bb) ST(); // new():在指定空间上创建对象

    pt->x = 1;
    pt->y = 2;

    cout << bb[0] << endl;
    cout << bb[1] << endl;

    pt->~ST(); // 显示的调用析构函数
}

int main(int argc, char *argv[])
{
    ex_func_3();

    return 0;
}

运行结果为:

[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out
1
2

实验结论:

  • 不是所有的编译器都遵循C++的标准规范
  • 编译器可能重定义 new 的实现,并在实现中抛出 bad_alloc 异常
  • 编译器的默认实现中,可能没有设置全局的 new_handler() 函数
  • 对于移植性要求较高的代码,需要考虑 new 的具体细节

3.小结

  • 不同的编译器在动态内存分配上的实现细节不同
  • malloc 函数在内存申请失败时返回NULL值
  • new 关键字在内存申请失败时
    1. 可能返回NULL值
    2. 可能抛出 bad_alloc 异常

原文地址:https://www.cnblogs.com/PyLearn/p/10104322.html

时间: 2024-08-30 05:34:34

C++解析-外传篇(3):动态内存申请的结果的相关文章

外传篇3 动态内存申请的结果

1. 必须知道的事实 (1)常见的动态内存分配代码 //C代码 int* p = (int*)malloc(10 * sizeof(int)); if(p != NULL){ //... } //C++代码 int* p = new int[10]; if(p != NULL){ //... } (2)必须知道的事实 ①malloc函数申请失败时,返回NULL值. ②new关键字申请失败时,则会根据编译器的不同,有的返回NULL值,而有的抛出std::bad_alloc异常. 2. new op

外传三 动态内存申请的结果

问题: 动态内存申请一定成功吗? 问题: new语句中的异常是怎么抛出来的? 一般我们会在new_handler函数中进行内存的整理,整理之后再次申请. 问题: 如何跨编译器统一new的行为,提高代码移植性? 全局定义new就是全局new操作符的重载. 最后两种方法是推荐的做法. 使用nothrow时,new失败了会返回空指针. 示例程序: 1 #include <iostream> 2 #include <new> 3 #include <cstdlib> 4 #in

C++函数中,两个自动释放内存的动态内存申请类

最近做一个事情,实现一个流程交互,其中主交互流程函数中,涉及较多的内存申请, 而健康的函数,都是在函数退出前将手动申请不再需要的内存释放掉, 使用很多方法,都避免不了较多的出错分支时,一堆的if free/delete,代码长而且不好管理 因此,利用C++对象离开作用域会自动调用析构函数的特点,在这儿实现了两个自动释放内存的动态内存申请类 第一个类,只管理内存,不并管理对象 #include <vector> class XAutoFreeMem { protected: std::vecto

C++解析(25):关于动态内存分配、虚函数和继承中强制类型转换的疑问

0.目录 1.动态内存分配 1.1 new和malloc的区别 1.2 delete和free的区别 2.虚函数 2.1 构造函数与析构函数是否可以成为虚函数? 2.2 构造函数与析构函数是否可以发生多态? 3.继承中的强制类型转换 4.小结 1.动态内存分配 1.1 new和malloc的区别 new关键字与malloc函数的区别: new关键字是C++的一部分 malloc是由C库提供的函数 new以具体类型为单位进行内存分配 malloc以字节为单位进行内存分配 new在申请内存空间时可进

C语言动态内存的申请和释放

什么是动态内存的申请和释放? 当程序运行到需要一个动态分配的变量时,必须向系统申请取得堆中的一块所需大小的存储空间,用于存储该变量.当不再使用该变量时,也就是它的生命结束时,要显式释放它所占用的存储空间,这样系统就能对该堆空间进行再次分配,做到重复使用有限的资源. 下面将介绍动态内存申请和释放的函数 1.malloc函数 在C语言中,使用malloc函数来申请内存.函数原型如下: #include<stdlib.h> void *malloc(size_t size); 参数size代表需要动

内存申请和释放及堆连续

glibc 内存申请和释放及堆连续检查 C语言有两种内存申请方式: 1.静态申请:当你声明全局或静态变量的时候,会用到静态申请内存.静态申请的内存有固定的空间大小.空间只在程序开始的时候申请一次,并且不再释放(除非程序结束). 2.自动申请:当你声明自动变量的时候会使用自动申请.函数参数.局部变量都属于自动变量.这些变量空间在程序执行致相关语句块申请,离开语句块时释放. 还有一种内存申请方式:动态内存申请.C语言变量并不支持动态内存申请,这一功能由库函数实现.C里面没有动态这个存储类型!! 当你

FreeRTOS 动态内存管理

本章节为大家讲解 FreeRTOS 动态内存管理,动态内存管理是 FreeRTOS 非常重要的一项功能,前面章节讲解的任务创建. 信号量. 消息队列. 事件标志组. 互斥信号量. 软件定时器组等需要的 RAM 空间都是通过动态内存管理从 FreeRTOSConfig.h 文件定义的 heap 空间中申请的. 动态内存管理介绍FreeRTOS 支持 5 种动态内存管理方案,分别通过文件 heap_1,heap_2,heap_3,heap_4 和 heap_5实现,这 5 个文件在 FreeRTOS

C语言学习笔记--动态内存分配

1. 动态内存分配的意义 (1)C 语言中的一切操作都是基于内存的. (2)变量和数组都是内存的别名. ①内存分配由编译器在编译期间决定 ②定义数组的时候必须指定数组长度 ③数组长度是在编译期就必须确定的 (3)但是程序运行的过程中,可能需要使用一些额外的内存空间 2. malloc 和 free 函数 (1)malloc 和 free 用于执行动态内存分配的释放 (2)malloc 所分配的是一块连续的内存 (3)malloc 以字节为单位,并且返回值不带任何的类型信息:void* mallo

keil c51的内部RAM(idata)动态内存管理程序(转)

源:keil c51的内部RAM(idata)动态内存管理程序 程序比较简单,但感觉比较有意思,个人认为有一定应用价值,希望大家有更好的思路和方法,互相促进. 程序的基本思路是:在CPU堆栈指针SP以上的RAM区域,通过把堆栈指针SP上移若干个字节,把空出的RAM区域供用户使用,当用户在使用完后又可以把该RAM区域释放. 头文件dmalloc51.h /* **********************************************************************