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

时间:2022-03-20 08:47:36

0.目录

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

2.new_handler() 函数

3.小结

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

问题:

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

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

C代码:

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

C++代码:

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

必须知道的事实!

  • 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() 的定义和使用:

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

问题:

如何跨编译器统一 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 异常