本文共 7363 字,大约阅读时间需要 24 分钟。
公司不让用盗版,遂准备逐一将各软件要么换成开源的,要么就自己写,看了看,就数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 variableBitmap * 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 variableBitmap * 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 _DEBUGOK,问题已解决,其实这只是个重载 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 .转载地址:http://qqxqj.baihongyu.com/