今天在用GDI+写程序时,有
HatchBrush * brushDotDiamond =
new
HatchBrush
(HatchStyle25Percent,color);
用VC6 SP6或VS2005编译错误为error C2660: 'new' : function does not take 3 parameters
这是VC的一个BUG,微软至今还没有解除。
解决办法如下:
法一:在该CPP文件开头部分注释掉#define new DEBUG_NEW
#ifdef
_DEBUG
//#define new DEBUG_NEW
#undef
THIS_FILE
static
char
THIS_FILE
[] = __FILE__;
#endif
建议法二:在GdiplusBase.h文件中 class GdiplusBase中添加如下代码
//////////////////////////////////////////////////////////////////////////
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);
}
//////////////////////////////////////////////////////////////////////////
下面是转载文章,作者:billdavid
不让用盗版,遂准备逐一将各软件要么换成开源的,要么就自己写,看了看,就数Acdsee最简单了(有些高级功能根本用不着),行,从这个入手吧。
需求分析:基本的图片查看功能,图片格式转换功能,基本的图形变换功能。
技术可行性分析:MS提供的GDI + 已经提供了比较专业的图形显示、格式转换功能,而且简单易用。
....
OK,就绪,开始干吧。
但是在程序编写的过程中,有条错误信息让我很不解。程序中有如下语句:
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
附:
( 转贴 )C ++的各种 new简介
1. new T
第一种 new 最简单, 调用类的( 如果重载了的话) 或者全局的operator new 分配空间, 然后用类型后面列的参数来调用构造函数, 用法是
new TypeName( initial_args_list).
如果没有参数 ,括号一般可以省略 .例如
int *p =new int;
int * p= new int (10 );
int * p= new foo ("hello" );
通过调用 delete来销毁 :
delete p ;
2. new T[]
这种 new 用来创建一个动态的对象数组 , 他会调用对象的operator new []来分配内存 (如果没有则调用 operator new, 搜索顺序同上), 然后调用对象的31m默认构造函数初始化每个对象用法:
new TypeName [num_of_objects ];
例如
int *p = new int[ 10];
销毁时使用 operator delete31m []
3. new ()T 和 new() T[]
这是个带参数的 new, 这种形式的new 会调用 operator new ( size_t, OtherType) 来分配内存, 这里的OtherType要和new 括号里的参数的类型兼容, 这种语法通常用来在某个特定的地址构件对象, 称为placement new ,前提是 operator new( size_t, void*) 已经定义, 通常编译器已经提供了一个实现, 包含< new> 头文件即可, 这个实现只是简单的把参数的指定的地址返回, 因而new ()运算符就会在括号里的地址上创建对象 .
需要说明的是 ,第二个参数不是一定要是 void *, 可以识别的合法类型, 这时候由C++ 的重载机制来决定调用那个operator new .
当然 ,我们可以提供自己的 operator new ( size_, Type), 来决定new 的行为 , 比如
char data[ 1000][ sizeof( foo)];
inline void * operator new (size_t ,int n)
{
return data [n ];
}
就可以使用这样有趣的语法来创建对象 :
foo *p =new (6 ) foo (); //把对象创建在data的第六个单元上的确很有意思
标准库还提供了一个nothrow的实现 :
void * operator new( std:: size_t, const std ::nothrow_t &) throw ();
void * operator new[]( std:: size_t, const std ::nothrow_t &) throw ();
就可以实现调用 new失败时不抛出异常
new (nothrow ) int (10 );
// nothrow 是 std::nothrow_t的一个实例
placement new 创建的对象不能直接 delete来销毁 ,而是要调用对象的析够函数来销毁对象 ,至于对象所占的内存如何处理 ,要看这块内存的具体来源 .
4. operator new (size_t )
这个的运算符分配参数指定大小的内存并返回首地址 ,可以为自定义的类重载这个运算符 ,方法就是在类里面声明加上
void *operator new (size_t size )
{
// 在这里分配内存并返回其地址
}
无论是否声明 ,类里面重载的各种 operator new 和 operator delete 都是具有 static 属性的 .
一般不需要直接调用 operator new, 除非直接分配原始内存( 这一点类似于C的malloc), 在冲突的情况下要调用全局的operator 加上:: 作用域运算符:
:: operator new (1000 ); // 分配1000个 31m字节
返回的内存需要回收的话 ,调用对应的 operator delete
5. operator new [](size_t )
这个也是分配内存 ,,只不过是专门针对数组 ,也就是 new T []这种形式 ,当然 ,需要时可以显式调用
6. operator new( size_t size, OtherType other_value)
和 operator new[]( size_t size, OtherType other_value)
参见上面的 new ()
需要强调的是 ,new 用来创建对象并分配内存, 它的行为是不可改变的, 可以改变的是各种operator new ,我们就可以通过重载 operator new 来实现我们的内存分配方案 .
参考资料:
1. PRB: Microsoft Foundation Classes DEBUG_NEW Does Not Work with GDI+. http: //support.microsoft.com/default.aspx?scid=kb;en-us;317799
2. VC++ 6.0中内存泄漏检测 . http ://blog.vckbase.com/bruceteen/archive/2004/10/28/1130.aspx
3. More Effective C++. Item 8 : Understand the different meanings of new and delete .