new和DEBUG_NEW

本文探讨了MFC程序中DEBUG_NEW宏定义与GDI+库中Bitmap类构造函数之间的冲突问题,详细解释了如何通过正确重载operator new解决此问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

但是在程序编写的过程中,有条错误信息让我很不解。程序中有如下语句:
bmPhoto   =   new   Bitmap(   THUMBNAIL_WIDTH,   THUMBNAIL_HEIGHT,   PixelFormat24bppRGB   );
每次DEBUG编译的时候总是报告如下的错误:
error   C2660:   'new '   :   function   does   not   take   3   parameters
开始以为是Bitmap的构造函数的问题,但是查了一下,Bitmap明明有个构造函数:
Bitmap(IN   INT   width,
              IN   INT   height,
              IN   PixelFormat   format   =   PixelFormat32bppARGB);
那会是什么问题呢?上网讨论了一下,最终将问题锁定在MFC程序中的这样一个宏定义上:
#ifdef   _DEBUG
#define   new   DEBUG_NEW
#undef   THIS_FILE
static   char   THIS_FILE[]   =   __FILE__;
#endif
这几行从来都不会引起我们注意的代码有什么问题呢?为什么会使得我们的代码报告如上所述的编译错误呢?
让我们来看看DEBUG_NEW的定义(在afx.h中):
#if   defined(_DEBUG)   &&   !defined(_AFX_NO_DEBUG_CRT)

//   Memory   tracking   allocation
void*   AFX_CDECL   operator   new(size_t   nSize,   LPCSTR   lpszFileName,   int   nLine);
#define   DEBUG_NEW   new(THIS_FILE,   __LINE__)
#if   _MSC_VER   > =   1200
void   AFX_CDECL   operator   delete(void*   p,   LPCSTR   lpszFileName,   int   nLine);
#endif
看到这里你可能会想,new被define成了DEBUG_NEW,而后者又被define成了new(...),这不是成了个循环?非也。由于 afx.h早于任何其它头文件被包含(stdafx.h包含afxwin.h,afxwin.h又包含了afx.h,而MFC要求我们在任何有效代码之前 包含stdafx.h,当然,这不是必须的),所以DEBUG_NEW的定义早于后面的#define   new   DEBUG_NEW,也就是说这个define只对后面的代码有效,对前面已经include了的afx.h中的代码是无效的。

上面只是题外话,现在回到正题。
MFC重载operator   new,是为了方便定位内存泄漏,重载后的operator   new会记录下所分配的每块内存对应的__FILE__和__LINE__信息。一般来讲,标准的operator   new的声明如下:
void   *__cdecl   operator   new(size_t);
即它只有一个参数,只接收一个size信息。我们的如下代码
int*   pi   =   new   int;   //   the   same   as   int*   pi   =   new   int();   or   int*   pi   =   new   int[1];
等价于
int*   tpi   =   (int*)operator   new(sizeof(int));   //   attention:   this   line   cannot   pass   compilation   if   you   have   define   DEBUG_NEW
int*   pi   =   tpi;
同理,定义DEBUG_NEW前,文章开头报错的这条语句:
Bitmap*   bmPhoto   =   new   Bitmap(   THUMBNAIL_WIDTH,   THUMBNAIL_HEIGHT,   PixelFormat24bppRGB   );
等价于
Bitmap*   tbmPhoto   =   (Bitmap*)operator   new(sizeof(Bitmap));
tbmPhoto-> Bitmap(   THUMBNAIL_WIDTH,   THUMBNAIL_HEIGHT,   PixelFormat24bppRGB   );   //   initialize   variable
Bitmap*   bmPhoto   =   tbmPhoto;
但是现在,由于DEBUG_NEW使用的是被重载的operator   new:
void*   AFX_CDECL   operator   new(size_t   nSize,   LPCSTR   lpszFileName,   int   nLine);
上述代码等价于:
Bitmap*   tbmPhoto   =   (Bitmap*)operator   new(sizeof(Bitmap),   __FILE__,   __LINE__);
tbmPhoto-> BitmaPBitmap(   THUMBNAIL_WIDTH,   THUMBNAIL_HEIGHT,   PixelFormat24bppRGB   );   //   initialize   variable
Bitmap*   bmPhoto   =   tbmPhoto;
回过头来看gdiplus.h中的operator   new的声明(在GdiplusBase.h中):
class   GdiplusBase
{
public:
        void   (operator   delete)(void*   in_pVoid)
        {
              DllExports::GdipFree(in_pVoid);
        }
        void*   (operator   new)(size_t   in_size)
        {
              return   DllExports::GdipAlloc(in_size);
        }
        void   (operator   delete[])(void*   in_pVoid)
        {
              DllExports::GdipFree(in_pVoid);
        }
        void*   (operator   new[])(size_t   in_size)
        {
              return   DllExports::GdipAlloc(in_size);
        }
};
它重载了operator   new,并且没有提供一个可以容纳3个参数的operator   new,同时基于这样一个事实:
不同命名域(指全局命名空间与有名命名空间之间,父类与子类,全局与类内部)内进行重载时,下一级的命名空间会覆盖掉上一级的定义,除非显示调用上一级的定义。
因此,全局的重新定义的operator   new并不能用于Bitmap类。也正因为这一原因,编译器会报告:
Bitmap*   tbmPhoto   =   (Bitmap*)Bitmap::operator   new(sizeof(Bitmap),   __FILE__,   __LINE__);
error   C2660:   'new '   :   function   does   not   take   3   parameters
知道了这一点,要修正这一问题,只需给class   GdiplusBase多重载几个operator   new即可。修正后的class   GdiplusBase如下:
#ifdef   _DEBUG

namespace   Gdiplus
{
        namespace   DllExports
        {
                #include   <GdiplusMem.h>
        };

        #ifndef   _GDIPLUSBASE_H
        #define   _GDIPLUSBASE_H
        class   GdiplusBase
        {
                public:
                        void   (operator   delete)(void*   in_pVoid)
                        {
                                DllExports::GdipFree(in_pVoid);
                        }

                        void*   (operator   new)(size_t   in_size)
                        {
                                return   DllExports::GdipAlloc(in_size);
                        }

                        void   (operator   delete[])(void*   in_pVoid)
                        {
                                DllExports::GdipFree(in_pVoid);
                        }

                        void*   (operator   new[])(size_t   in_size)
                        {
                                return   DllExports::GdipAlloc(in_size);
                        }

                        void   *   (operator   new)(size_t   nSize,   LPCSTR   lpszFileName,   int   nLine)
                        {
                                return   DllExports::GdipAlloc(nSize);
                        }

                        void   operator   delete(void*   p,   LPCSTR   lpszFileName,   int   nLine)
                        {
                                DllExports::GdipFree(p);
                        }

                };
        #endif   //   #ifndef   _GDIPLUSBASE_H
}
#endif   //   #ifdef   _DEBUG
OK,问题已解决,其实这只是个重载operator   new的问题,但这个问题由于DEBUG_NEW这个不起眼的宏,倒还真变得有点复杂。

最后总结一下,在进行operator   new重载时应注意:
1.new   operator是不可以重载的,可以重载的是operator   new。new   operator   首先调用   operator   new,然后调用构造函数(如果有的话)。new   operator的这个行为是不可以重载的,可以重载的仅仅是operator   new,也就是内存分配。
2.重载operator   new是一件必须十分小心的事情,在编写MFC程序或者你所编写的系统重载了全局的operator   new时,尤其需要注意,同时应注意所有的#include头文件最好添加在所有define之前,以免造成受到后续对new的重定义的影响。你可以尝试 在你的MFC程序的#define   new   DEBUG_NEW一句之后,添加#include   <vector> ,你会收到一大堆莫名奇妙的错误提示(DEBUG编译时才有),这正是由于#define   new   DEBUG_NEW和后面的static   char   THIS_FILE[]   =   __FILE__;造成的影响。
3.operator   new/delete在性质上类似于静态函数,你可以直接通过类名来访问它们。
4.理解了operator   new的基本概念,要理解头文件NEW中的placement   new/delete的实现也就不是什么难事了,头文件NEW中的placement   new/delete的实现如下:
#ifndef   __PLACEMENT_NEW_INLINE
#define   __PLACEMENT_NEW_INLINE
inline   void   *__cdecl   operator   new(size_t,   void   *_P)
        {return   (_P);   }
#if           _MSC_VER   > =   1200
inline   void   __cdecl   operator   delete(void   *,   void   *)
        {return;   }
#endif
#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值