• 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;
    }
    

    运行结果为:

    [root@bogon 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)
    [root@bogon 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;
    }
    

    运行结果为:

    [root@bogon 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)
    [root@bogon 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;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon 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;
    }
    

    运行结果为:

    [root@bogon Desktop]# g++ test.cpp
    [root@bogon Desktop]# ./a.out 
    1
    2
    

    实验结论:

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

    3.小结

    • 不同的编译器在动态内存分配上的实现细节不同
    • malloc 函数在内存申请失败时返回NULL
    • new 关键字在内存申请失败时
      1. 可能返回NULL值
      2. 可能抛出 bad_alloc 异常
  • 相关阅读:
    常见 Web 安全攻防总结
    传统方式接口测试返回值json验证
    Springboot中RestTemplate -- 用更优雅的方式发HTTP请求
    mock简单的json返回
    MySQL数据库学习笔记(五)----MySQL字符串函数、日期时间函数
    MySQL数据库学习笔记(四)----MySQL聚合函数、控制流程函数(含navicat软件的介绍)
    MySQL数据库学习笔记(三)----基本的SQL语句
    MySQL数据库学习笔记(一)----MySQL 5.6.21的安装和配置(setup版)
    python实现广度优先搜索
    php递归
  • 原文地址:https://www.cnblogs.com/PyLearn/p/10104322.html
Copyright © 2020-2023  润新知