但是在程序编写的过程中,有条错误信息让我很不解。程序中有如下语句:
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
new和DEBUG_NEW
最新推荐文章于 2024-08-14 11:12:40 发布