您的位置:首页 > 其它

微软VC各版本BUG: error C2660: 'new' : function does not take 3 parameters

2013-03-09 14:06 302 查看
今天在用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 charTHIS_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
.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐