在VC6.0中如何让new操作失败后抛出异常?

VC6.0打开文件错误解决方案详解 在使用VC6.0进行开发时,打开项目文件或工程文件时常常会遇到各类错误。这些错误主要包括:文件无法加载、工程配置异常、编译失败、资源文件缺失等。这些问题的根源往往与路径配置不当、依赖库缺失或开发环境兼容性不良有关。例如,当工程文件路径中包含中文或空格时,VC6.0可能会因路径解析失败而无法加载项目。此外,若工程所依赖的MFC库或ActiveX控件未正确注册或缺失,也会导致编译失败或运行时异常。 阅读详情

    标准C++规定new一个对象时如果分配内存失败就应抛出一个std::bad_alloc异常,如果不希望抛出异常而仅仅传回一个NULL指针,可以用new的无异常版本:new(nothrow)。

    VC6.0在<new>头文件中声明了这两种operator new操作符:

void *__cdecl operator new(size_t) _THROW1(std::bad_alloc);
void *__cdecl operator new(size_t, const std::nothrow_t&) _THROW0();

并分别定义在newop.cpp和newop2.cpp中。而_THROW0和_THROW1则是两个宏,在Include目录的xstddef文件中定义:

#define _THROW0()   throw ()
#define _THROW1(x)  throw (x)

newop.cpp和newop2.cpp对应的目标模块被打包进标准C++库中。标准C++库有若干个版本: libcp.lib(单线程静态版)、libcpd.lib(单线程静态调试版)、libcpmt.lib(多线程静态版)、libcpmtd.lib(多线程静态调试版)、msvcprt.lib(多线程动态版的导入库),msvcprtd.lib(多线程动态调试版的导入库),这些库与相应版本的C标准库一起使用,比如libcp.lib与libc.lib搭配。另外,VC6.0在new.cpp还定义了一个operator new,原型如下 :

void * operator new( unsigned int cb )

而new.cpp对应的目标模块却是被打包进C标准库中的(是不是有点奇怪?)。

    一般来说,程序员不会显式指定链接C++标准库,可是当程序中确实使用了标准C++库时链接器却能聪明地把相应的C++标准库文件加进输入文件列表,这是为什么?其实任何一个C++标准头文件都会直接或间接地包含use_ansi.h文件,打开它一看便什么都清楚了(源码之前,了无秘密) :

/***
*use_ansi.h - pragmas for ANSI Standard C++ libraries
*
*       Copyright (c) 1996-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
*       This header is intended to force the use of the appropriate ANSI
*       Standard C++ libraries whenever it is included.
*
*       [Public]
*
****/


#if     _MSC_VER > 1000
#pragma once
#endif

#ifndef _USE_ANSI_CPP
#define _USE_ANSI_CPP

#ifdef _MT
#ifdef _DLL
#ifdef _DEBUG
#pragma comment(lib,"msvcprtd")
#else // _DEBUG
#pragma comment(lib,"msvcprt")
#endif // _DEBUG

#else // _DLL
#ifdef _DEBUG
#pragma comment(lib,"libcpmtd")
#else // _DEBUG
#pragma comment(lib,"libcpmt")
#endif // _DEBUG
#endif // _DLL

#else // _MT
#ifdef _DEBUG
#pragma comment(lib,"libcpd")
#else // _DEBUG
#pragma comment(lib,"libcp")
#endif // _DEBUG
#endif

#endif // _USE_ANSI_CPP


    现在我们用实际代码来测试一下new会不会抛出异常,建一个test.cpp源文件:

// test.cpp
#include <new>
#include <iostream>
      
using namespace std;
      
class BigClass 
{
public:
   BigClass() {}
   ~BigClass(){}
   char BigArray[0x7FFFFFFF];
};
      
int main()
{
   try 
   {
      BigClass *p = new BigClass;
   }
   catch( bad_alloc &a) 
   {
      cout << "new BigClass, threw a bad_alloc exception" << endl;
   }
      
   BigClass *q = new(nothrow) BigClass;
   if ( q == NULL )
      cout << "new(nothrow) BigClass, returned a NULL pointer" << endl;

   try 
   {
      BigClass *r = new BigClass[1];
   }
   catch( bad_alloc &a) 
   {
      cout << "new BigClass[1], threw a bad_alloc exception" << endl;
   }

   return 0;
}

根据VC6.0编译器与链接器的做法(请参考《为什么会出现LNK2005"符号已定义"的链接错误?》),链接器会首先在C++标准库中解析符号,然后才是C标准库,所以如果开发者没有自定义operator new的话最后程序链接的应该是C++标准库中newop.obj和newop2.obj模块里的代码。可是程序运行的结果却是:

new(nothrow) BigClass, returned a NULL pointer

显然程序始终未抛出bad_alloc异常。单步跟踪观察,发现第1个和第3个new实际上调用了new.cpp里的operator new,而第二个new(nothrow)则正确地调用了newop2.cpp定义的版本。很难理解是吧?但是当你用

dumpbin /SYMBOLS libcp.lib

dump出libcp.lib所有的符号信息时,你会发现其中的newop.obj模块没有定义任何符号(其它版本也一样)。不可思议!newop.cpp的实现代码明明写在那儿,怎么会....?让我们再仔细看看newop.cpp,咦,operator new的定义被包裹在一个#if...#endif块中:

#if !defined(_MSC_EXTENSIONS)

...
...

void *__cdecl operator new(size_t size) _THROW1(_STD bad_alloc)
{
   ...
   ...
}

#endif

哦,原来需要_MSC_EXTENSIONS宏未定义,实现代码才是有效的啊。那么这个宏是什么意思?其实Visual C++在语言层面上对ANSI C标准做了一些特殊的扩展,定义_MSC_EXTENSIONS意味着编译器支持这样的扩展,没有定义它编译器就会严格按照ANSI C标准来编译程序。实际上如果指定了编译选项/Ze编译器就会自动定义这个宏,指定/Za则不会,而且/Ze是缺省选项。作者猜想Visual Studio的开发人员在build标准C++库时很可能没有指定/Za,导致newop.cpp中的operator new定义被无情抛弃。是他们的疏漏吗?我看未必,大家可以试试用/Za选项去编译那些标准库文件,看看有多少编译不通过。VC标准库的实现用了很多微软扩展的语言特性,不指定/Za是情有可原的,我不明白的是newop.cpp的作者(好象是P.J. Plauger老人家)为什么会加上一个如此愚蠢的"#if !defined(_MSC_EXTENSIONS)",因为实在看不出这个operator new定义与_MSC_EXTENSIONS有什么冲突的地方。

    既然标准C++库里的newop.obj是个空壳,那我们就只好自己动手丰衣足食了。把newop.cpp和dbgint.h(都在VC98/crt/src目录下)拷贝到test.cpp所在的目录,并将newop.cpp中的

#include <dbgint.h>

改成

#include "dbgint.h"

然后用

cl /c /Za /D_CRTBLD newop.cpp

编译它。/D_CRTBLD定义了_CRTBLD宏,为什么这么做呢?因为dbgint.h属于内部头文件,VC不希望应用程序用到它,便在文件中埋伏了这么一段:

#ifndef _CRTBLD
/*
 * This is an internal C runtime header file. It is used when building
 * the C runtimes only. It is not to be used as a public header file.
 */
#error ERROR: Use of C runtime library internal header file.
#endif
  /* _CRTBLD */

可我们确确实实是想build标准库(的一部分),所以只好强行突破这个限制了。然后编译test.cpp:

cl /c /GX test.cpp

最后进行链接:

link test.obj newop.obj

这时再运行test.exe输出的结果就是

new BigClass, threw a bad_alloc exception
new(nothrow) BigClass, returned a NULL pointer
new BigClass[1], threw a bad_alloc exception

    值得庆幸的是虽然VC6.0如此弱智,但VC7.1却表现良好,原因是VC7.1的newop.cpp和newaop.cpp(数组版)取消了那个愚的"#if !defined(_MSC_EXTENSIONS)",于是标准C++库中的newop.obj和newaop.obj模块都实实在在地有了相应代码。另外,nothrow版的定义也分别转移到了newopnt.cpp和newaopnt.cpp中。

    后记: 作者在2001年便碰到过这个问题,百思不得其解,于是在优快云论坛上发问,也不见答复。从此便搁置一旁,直到最近因探究LNK2005链接错误而彻底弄清楚VC链接器解析符号的规则后,才意识到二者或有联系。于是重拾旧疑,顺藤而上,果然问题就迎刃而解。此题虽小,功夫却做足,最后总算水落石出,解除了4年的积惑。

VC6.0串口通信程序设计与实战 本文还有配套的精品资源,点击获取 简介:串口通信广泛应用于工业控制等领域,本文以"CommTest"为例,详细讲解基于VC6.0进行串口通信的程序设计。通过... 阅读详情

相关推荐

C语言-库函数-源码.....

C语言库函数源码

用Crt系列函数分析windows程序的内存泄露

近几日分析的windows版本一个内存泄露故障,之前之后补了下功课,记录如下,分近10次才补充完整,可能不太连贯。

朱金华的专栏 3362

从零开始安装 Windows 11 和 Ubuntu 22.04 双系统

本文详解Windows与Ubuntu 22.04双系统安装流程:需提前确认BIOS模式(UEFI/GPT或Legacy/MBR),关闭BitLocker加密、设备加密及快速启动;推荐Rufus/Ventoy制作启动盘。安装时需规划分区方案(建议单独划分EFI系统分区),注意引导器安装位置选择。双系统启动通过BIOS引导顺序管理,安装后需同步Windows与Linux系统时间。教程适配主流UEFI+GPT设备,强调操作前数据备份及Windows更新策略调整,避免兼容性问题。

萑澈的博客 3767

C++调试堆

内存管理和调试堆 描述堆函数的“Debug”版本。这些函数解决两个最难处理的内存分配问题:改写已分配缓冲区的结尾和内存泄漏(当不再需要分配后未能释放它们)。 调试堆中的块类型 描述在调试堆中内存块所分配到的五种分配类型。出于泄漏检测和状态报告的目的,以不同方式对这些分配类型进行跟踪和报告。 调试堆 提供有关使用调试堆的信息。信息包括:哪些调用用于“Debug”版本,释放内存块时将发生什么,哪些调试

痞子龙3D编程 1640

RK3588开发板量产前必做:24小时DDR压力测试到底在测什么?(附ArmSoM-W3测试脚本解析)

本文深入解析RK3588开发板量产前的24小时DDR压力测试,揭示其核心价值与技术原理。通过memtester、stressapptest和变频测试的组合使用,全面验证DDR内存稳定性。特别介绍ArmSoM-W3测试脚本的创新设计,包括并行测试架构和异常熔断机制,为开发者提供可靠的量产测试方案。

weixin_30325487的博客 454

Android Audio - 支持多应用同时录音_Android8.1修改方法

支持多应用同时录音_Android8.1修改方法 源码路径: sdk\frameworks\av\services\audiopolicy\managerdefault\AudioPolicyManager.cpp 需要修改两个位置: audio_io_handle_t AudioPolicyManager::getInputForDevice() 在mpClientInterface-&gt...

iFinelio Tower 的博客 5758

VC6.0中如何让new操作失败后抛出异常?

原文:http://dev.yesky.com/370/2149370.shtml标准C++规定new一个对象时如果分配内存失败就应抛出一个std::bad_alloc异常,如果不希望抛出异常而仅仅传回一个NULL指针,可以用new的无异常版本:new(nothrow)。   VC6.0在头文件中声明了这两种operator new操作符:void *__cdecl opera

桂林哈秋 - 我想学编程 681

基于Arduino与光电传感器的心率监测系统:从原理到实现的完整指南

光电式心率监测是一种基于光电容积脉搏波描记法(PPG)的非侵入式生命体征测量技术。其核心原理是利用特定波长光线照射皮肤组织,通过检测因血液容积周期性变化导致的反射光强微弱波动,来获取心率信息。这项技术的关键价值在于实现了低成本、便携化的连续生理参数监测,广泛应用于健康管理、运动科学及嵌入式系统教学领域。在工程实践中,信号处理算法(如移动平均滤波与动态阈值峰值检测)对提升测量精度至关重要。本文以Arduino平台为例,详细解析了如何集成MAX30102或Pulse Sensor Amped等光电传感器,结合T

weixin_33461476的博客 245

VC6.0中如何让new操作失败后抛出异常?(转载)

VC6.0中如何让new操作失败后抛出异常? 2005-10-28 11:00 作者: soloist  出处: BLOG   标准C++规定new一个对象时如果分配内存失败就应抛出一个std::bad_alloc异常,如果不希望抛出异常而仅仅传回一个NULL指针,可以用new的无异常版本:new(nothrow)。   

cad_vc的专栏 559

3D Slicer中安装MedSAM插件

大家应该都读过这篇论文:《Segment Anything in Medical Images》这个插件便是该论文团队推出的在3D Slicer上使用的插件,可以分割医学图像的任何感兴趣区域。

m0_73719342的博客 2330

国科大作业考试资料《人工智能原理与算法》2024新编-第三次作业整理

1. 你的目标是让机器人走出迷宫。机器人面朝北,开始位置在迷宫中间。你可以让机器人转向面朝东、南、西或北。你可以让机器人向前走一段距离,在撞墙之前它会停步。 (1)将问题形式化。状态空间有多大? (2)在迷宫中游走,在两条路或更多路交叉的路口可以转弯。重新形式化这个问题。现在状态空间有多大? (3)从迷宫的任一点出发,我们可以朝四个方向中的任一方向前进直到可以转弯的地方,而且我们只需要这样做。重新对这个问题进行形式化。我们需要记录机器人的方向吗? 2. 传教士和野人问题

MatsumotoChrikk 984

VC6.0中如何让new操作失败后抛出异常

标准C++规定new一个对象时如果分配内存失败就应抛出一个std::bad_alloc异常,如果不希望抛出异常而仅仅传回一个NULL指针,可以用new的无异常版本:new(nothrow)。   VC6.0在头文件中声明了这两种operator new操作符: void *__cdecl operator new(size_t) _THROW1(std::bad_all

fjclc2008的专栏 622

基于扩散模型的图像去噪与修复:原理、实现与性能分析【附核心代码】

扩散模型(Diffusion Models)在图像修复、去噪、风格化、超分辨率等领域的应用,展示了其强大的生成能力和灵活性。通过模拟图像逐步加噪声再去噪的过程,扩散模型不仅能够恢复图像的细节,还能生成高质量的图像。本文详细探讨了扩散模型在图像修复和增强中的应用,并通过具体代码示例展示了如何使用扩散模型进行去噪、风格化、图像超分辨率等任务。扩散模型在图像修复中通过逐步去噪修复损坏的区域,在图像去噪中则能够去除图像中的噪声并保留细节;在图像风格化中,扩散模型通过结合目标风格和内容生成富有艺术性的图像;

一键难忘的博客 2929

VC6.0如何让new失败后抛出异常

VC6.0如何让new失败后抛出异常【简 介】标准C++规定new一个对象时如果分配内存失败就应抛出一个std::bad_alloc异常,如果不希望抛出异常而仅仅传回一个NULL指针,可以用new的无异常版本:new(nothrow)。C标准库一起使用,比如libcp.lib与libc.lib搭配。另外,VC6.0在new.cpp还定义了一个operator new,原型如下 :void

编程爱好者 970

【微信小程序】微信小程序 manifest.json 配置 ES6 转 ES5 开关:3步让你的代码兼容性更强

ES6(ECMAScript 6)是 JavaScript 的最新标准,引入了许多新的语法和特性,如箭头函数、模板字符串、解构赋值等。然而,不是所有的环境都支持 ES6,特别是旧版本的浏览器和一些特定的运行环境。为了确保代码的兼容性,我们需要将 ES6 代码转换为 ES5 代码。通过本文的详细讲解,相信你已经学会了如何在文件中配置微信小程序的 ES6 转 ES5 开关。这个小小的配置步骤能够显著提高代码的兼容性和稳定性。如果你有任何问题或需要进一步的帮助,欢迎随时留言交流。

java专栏 1360

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

new操作符的使用问题[ask in smth]

new操作符的使用问题发信站: 水木社区 (Thu Oct  6 10:49:31 2005), 站内下面的程序是effective c++中用来说明new操作符的一段代码,但是我运行了一下,并没有得到预期的效果代码如下:void noMoreMemory(){  cerr   abort();}int main(){ set_new_handler(noMoreMemory); int *pBi

红羊家园 1462

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

VC6.0如何让new失败后抛出异常【转】

http://www.enet.com.cn/article/2005/1013/A20051013461516.shtml【简 介】  标准C++规定new一个对象时如果分配内存失败就应抛出一个std::bad_alloc异常,如果不希望抛出异常而仅仅传回一个NULL指针,可以用new的无异常版本:new(nothrow)。 C标准库一起使用,比如libcp.lib与li

lw02nju的专栏 980

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

C++ new失败处理

1.C++里new分配空间失败 C++里,new是最常用的分配堆空间的操作符。 new和malloc的区别体现在这几方面: 1)new是操作符,malloc是函数; 2)new和delete操作符对应,malloc则和free函数对应; 3)new可以直接调用构造函数,而malloc不可以; 4)new...

wangzhezhilu001的专栏 8660

VC6.0 启动失败

事情是这样的,zai

adru505的专栏 1071

VC new 失败小议

用VC++算是家常便饭了,但是今天才发现有下面这个问题:unsigned int uSize=0;char* p=new char[uSize];if(!p){ //异常处理。}//......调试结果发现这样做 p 会不等于 NULL (即0)。上网查了下,搜索"vc new 失败",得到了一系列的说法:C++旧标准:new (nothrow) operat

Jingle的专栏 3149
上一篇: 为什么会出现LNK2005"符号已定义"的链接错误?
下一篇: 程序设计语言正交特性的一点思考
ShenRui
博客等级 码龄20年 63粉丝 36原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值