C++解析-外传篇(3):动态内存申请的结果
0.目录
1.动态内存申请一定成功吗?
2.new_handler() 函数
3.小结
1.动态内存申请一定成功吗?
问题:
动态内存申请一定成功吗?
常见的动态内存分配代码:
C代码:

C++代码:

必须知道的事实!
- malloc函数申请失败时返回NULL值
- new关键字申请失败时(根据编译器的不同)
- 返回NULL值
- 抛出 std::bad_alloc 异常
问题:
new语句中的异常是怎么抛出来的?
new关键字在C++规范中的标准行为:
- 在堆空间申请足够大的内存
- 成功:
- 在获取的空间中调用构造函数创建对象
- 返回对象的地址
- 失败:
- 抛出 std::bad_alloc 异常
- 成功:
- new在分配内存时
- 如果空间不足,会调用全局的 new_handler() 函数
- new_handler() 函数中抛出 std::bad_alloc 异常
- 可以自定义 new_handler() 函数
- 处理默认的new内存分配失败的情况
2.new_handler() 函数
new_handler() 的定义和使用:

问题:
如何跨编译器统一 new 的行为,提高代码移植性?
解决方案:
- 全局范围(不推荐)
- 重新定义 new / delete 的实现,不抛出任何异常
- 自定义 new_handler() 函数,不抛出任何异常
- 类层次范围
- 重载 new / delete,不抛出任何异常
- 单次动态内存分配
- 使用 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 关键字在内存申请失败时
- 可能返回NULL值
- 可能抛出 bad_alloc 异常
C++解析-外传篇(3):动态内存申请的结果的更多相关文章
- C++函数中,两个自动释放内存的动态内存申请类
最近做一个事情,实现一个流程交互,其中主交互流程函数中,涉及较多的内存申请, 而健康的函数,都是在函数退出前将手动申请不再需要的内存释放掉, 使用很多方法,都避免不了较多的出错分支时,一堆的if fr ...
- C++中动态内存申请的结果
1,问题: 1,动态内存申请一定成功吗? 1,不一定成功: 2,常见的动态内存分配代码: 1,C 代码: * sizeof(int)); if( p != NULL ) { // ... ... } ...
- C++解析(25):关于动态内存分配、虚函数和继承中强制类型转换的疑问
0.目录 1.动态内存分配 1.1 new和malloc的区别 1.2 delete和free的区别 2.虚函数 2.1 构造函数与析构函数是否可以成为虚函数? 2.2 构造函数与析构函数是否可以发生 ...
- C++解析-外传篇(2):函数的异常规格说明
0.目录 1.异常规格说明 2.unexpected() 函数 3.小结 1.异常规格说明 问题: 如何判断一个函数是否会抛出异常,以及抛出哪些异常? C++提供语法用于声明函数所抛出的异常 异常声明 ...
- C++解析-外传篇(1):异常处理深度解析
0.目录 1.异常的最终处理 2.结束函数terminate() 3.小结 1.异常的最终处理 问题: 如果在main函数中抛出异常会发生什么? 如果异常不处理,最后会传到哪里? 下面的代码的输出什么 ...
- C 动态内存申请
例子: int *p=0; int number=0; scanf("%d",&number); p = (int*)malloc(number*sizeof(int));
- 【C/C++开发】C语言 DLL(动态链接库)中申请动态内存释放的问题
参考:首先,声明一点,凡是使用malloc之类命令动态申请的内存,必须进行释放操作,否则就会发生内存泄漏问题. DLL中申请的内存释放,如果没有做过,很可能会认为是直接在调用程序中释放就可以了,其实不 ...
- C语言动态内存的申请和释放
什么是动态内存的申请和释放? 当程序运行到需要一个动态分配的变量时,必须向系统申请取得堆中的一块所需大小的存储空间,用于存储该变量.当不再使用该变量时,也就是它的生命结束时,要显式释放它所占用的存储空 ...
- keil c51的内部RAM(idata)动态内存管理程序
程序比较简单,但感觉比较有意思,个人认为有一定应用价值,希望大家有更好的思路和方法,互相促进. 程序的基本思路是:在CPU堆栈指针SP以上的RAM区域,通过把堆栈指针SP上移若干个字节,把空出的RAM ...
随机推荐
- [BZOJ1185][HNOI2007]最小矩形覆盖-[凸包+旋转卡壳]
Description 传送门 Solution 感性理解一下,最小矩形一定是由一条边和凸包上的边重合的. 然后它就是模板题了..然而真的好难调,小于大于动不动就打错. Code #include&l ...
- 【BZOJ3144】[HNOI2013]切糕
[BZOJ3144][HNOI2013]切糕 题面 题目描述 经过千辛万苦小 A 得到了一块切糕,切糕的形状是长方体,小 A 打算拦腰将切糕切成两半分给小 B.出于美观考虑,小 A 希望切面能尽量光滑 ...
- centos7.4 防火墙设置
1.关闭默认的firewall防火墙 systemctl stop firewalld.service #停止firewall systemctl disable firewalld.service ...
- Drupal views 学习之初识
1. 简介 用过Yii框架的同学,应该都会用到过GridView和ListView组件.可以很方便的用网格或列表展示内容. 例如淘宝: 网格显示 列表显示 2. 使用view可以方便的配出类似上面的展 ...
- 【redis的链接】redis的两种连接方法
执行redis-server /etc/redis.conf开启服务 方法一: [root@zhangmeng ~]# redis-cli > > quit 方法二: [root@zhan ...
- robotframework 脚本编写规范
测试集.脚本 测试脚本的名字不要超过20个字符,文件类型应该为txt 名字必需易读且有意义(看名知意) 记住测试集的名字是自动根据文件.目录的名字创建的.后缀名会被截去,下划线会转换为空格,如果名 ...
- katalon系列十六:代码运行时实时创建元素对象或列表
Katalon的常规方法是先抓取元素并保存到仓库,在脚本中需要用到的时候调取,但假如元素属性和个数是可变的,就不能事先保存到仓库了,需要在脚本运行时实时创建. 代码运行时实时创建一个元素对象的例子im ...
- JAVA基础学习之路(十一)引用传递
引用传递: 不同栈内存可以指向同一块堆内存,不同栈内存可以对一块堆内存进行修改 范例一: class Message { private int num = 10; public Message(in ...
- MSCOCO - pycocoDemo 学习版
Reference: https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoDemo.ipynb https://git ...
- 你应该知道的PHP库
Libchart – 这也是一个简单的统计图库. JpGraph – 一个面向对象的图片创建类. Open Flash Chart – 这是一个基于Flash的统计图. RSS 解析 解释RSS并是一 ...