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

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

(1)问题:动态内存申请一定成功吗?

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

C代码:
    
C++代码:
    

(3)必须知道的事实!

  • malloc函数申请失败返回NULL值

  • new关键字申请失败时根据编译器的不同

    • 返回NULL值

    • 抛出 std::bad_alloc 异常

 

(4)问题: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() 函数

(1)new_handler() 的定义和使用:
      

(2)问题如何跨编译器统一new 的行为提高代码移植性

(3)解决方案:

  • 全局范围(不推荐)

    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

 

编程实验:VS2010 new实现

/***
*new.cxx - defines C++ new routine
*
*       Copyright (c) Microsoft Corporation.  All rights reserved.
*
*Purpose:
*       Defines C++ new routine.
*
*******************************************************************************/


#ifdef _SYSCRT
#include <cruntime.h>
#include <crtdbg.h>
#include <malloc.h>
#include <new.h>
#include <stdlib.h>
#include <winheap.h>
#include <rtcsup.h>
#include <internal.h>

void * operator new( size_t cb )
{
    void *res;

    for (;;) {

        //  allocate memory block
        res = _heap_alloc(cb);

        //  if successful allocation, return pointer to memory

        if (res)
            break;

        //  call installed new handler
        if (!_callnewh(cb))
            break;

        //  new handler was successful -- try to allocate again
    }

    RTCCALLBACK(_RTC_Allocate_hook, (res, cb, 0));

    return res;
}
#else  /* _SYSCRT */

#include <cstdlib>
#include <new>

_C_LIB_DECL
int __cdecl _callnewh(size_t size) _THROW1(_STD bad_alloc);
_END_C_LIB_DECL

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
        {       // try to allocate size bytes
        void *p;
        while ((p = malloc(size)) == 0)
                if (_callnewh(size) == 0)
                {       // report no memory
                static const std::bad_alloc nomem;
                _RAISE(nomem);
                }

        return (p);
        }

/*
 * Copyright (c) 1992-2002 by P.J. Plauger.  ALL RIGHTS RESERVED.
 * Consult your license regarding permissions and restrictions.
 V3.13:0009 */
#endif  /* _SYSCRT */

实验结论:

  • 不是所有的编译器都遵循C++的标准规范

  • 编译器可能重定义 new 的实现,并在实现中抛出 bad_alloc 异常

  • 编译器的默认实现中可能没有设置全局的 new_handler() 函数

  • 对于移植性要求较高的代码,需要考虑 new 的具体细节

 

3.小结

  • 不同的编译器动态内存分配上的实现细节不同

  • malloc 函数在内存申请失败时返回NULL

  • new 关键字在内存申请失败时

    1. 可能返回NULL

    2. 可能抛出 bad_alloc 异常

posted @ 2019-01-06 16:58  梦心之魂  阅读(218)  评论(0编辑  收藏  举报