??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
用位q算来表C把一个负数{换成zig-zag~码Q就?span class="pun">
int32是:(n << 1) ^ (n >> 31)
int64是:(n << 1) ^ (n >> 63)
也就是说Q如果是负数Q对?2位最多能省去30|其中1格是W号位,另一个代表最?Q此处假?#8220;正负0”不合法)。同理,64位最多能省去62位。当然比较极端的是所有的位数都被用上了?br>
]]>
首先什么是Wow64Q很多朋友一看到64p个方法是判断当前pȝ是否?4bit的,其实不然。Wow64是Windows-On-Windows64的意思,它是指在64位的操作pȝ上(不是?4位的CPUQ运?2位应用程序的兼容q_?/p>
下面是MSDN中一DIsWow64的应用程序:
BOOL IsWow64() { typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; BOOL bIsWow64 = FALSE; fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( GetModuleHandle(_T("kernel32")), "IsWow64Process"); if (NULL != fnIsWow64Process) { fnIsWow64Process(GetCurrentProcess(),&bIsWow64); } return bIsWow64; }
下面的代码用来检这个程序的l果Q?/p>
if( IsWow64() == TRUE ) { _tprintf(_T("IsWow64() == TRUE\n")); } else { _tprintf(_T("IsWow64() == FALSE\n")); }
让我们编译一下这个程序?/p>
我们需要的?4位的操作pȝQ比如XP64bitQWindows 2008 R2{都?4bit操作pȝ?/p>
?4位的操作pȝ上运行的kernel32.dll中,会实现IsWow64ProcessҎQ而在32位系l中提供的kernel32.dll中则没有提供相关函数的实现?/p>
比较qh人的则是bIsWow64Q其实仔l看MSDN中的RemarkQ会发现Q?/p>
If the application is a 64-bit application running under 64-bit Windows, the Wow64Process parameter is set to FALSE.也就是说64位应用程序跑?4位的操作pȝ上,bIsWow64的值将是FALSE而不是TRUE?
因此我们需要分别将我们的程序编译成Win32q_和x64q_的,如果你用Visual Studioq行~译Q默认安装则只包?2位的~译?链接器,即便你是?4位操作系l上安装Q也是一L。你需要在VC++节点下勾选x64选项才可以,Itanium则需要在Serverq_下安装才可勾选。然后在~译的时候,分别选择Win32和x64q行~译?
~译后,q行Q结果如我们分析的一P
?4位系l上q行Win32~译配置的结果是IsWow64() == TRUEQ而x64~译配置的结果是IsWow64() == FALSE?
如果惌知道当前pȝ是否?4位的Q则可以通过下面的方法:
BOOL Is64bitSystem() { SYSTEM_INFO si; GetNativeSystemInfo(&si); if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 ) { return TRUE; } else { return FALSE; } }
注意Q需要注意是GetNativeSystemInfo 函数从Windows XP 开始才有, ?IsWow64Process 函数?Windows XP with SP2 以及 Windows Server 2003 with SP1 开始才有?
动态连接库的创建步骤:
一、创建Non-MFC DLL动态链接库
1、打开File ?gt; New ?gt; Project选项Q选择Win32 Dynamic-Link Library ?gt;sample project ?gt;工程名:DllDemo
2、新Z?h文gDllDemo.h
#ifdef DllDemo_EXPORTS
#define DllAPI __declspec(dllexport)
#else
#define DllAPI __declspec(dllimport)
extern "C" //原样~译
{
DllAPI int __stdcall Max(int a,int b); //__stdcall佉KC/C++语言内能够调用API
}
#endif
3、在DllDemo.cpp文g中导入DllDemo.h文gQƈ实现Max(int,int)函数
#include "DllDemo.h"
DllAPI int __stdcall Max(int a,int b)
{
if(a==b)
return NULL;
else if(a>b)
return a;
else
return b;
}
4、编译程序生成动态连接库
二、用.def文g创徏动态连接库DllDemo.dll
1、删除DllDemo工程中的DllDemo.h文g?
2、在DllDemo.cpp文g_删除 #include DllDemo.h语句?
3、向该工E中加入一个文本文Ӟ命名为DllDemo.defq写入如下语句:
LIBRARY MyDll
EXPORTS
Max@1
4、编译程序生成动态连接库?
动态链接的调用步骤Q?/strong>
一、隐式调?/strong>
1、徏立DllCnslTest工程
2、将文gDllDemo.dll、DllDemo.lib拯到DllCnslTest工程所在的目录
3、在DllCnslTest.h中添加如下语句: #define DllAPI __declspec(dllimport) 4、在DllCnslTest.cpp文g中添加如下语句: #include "DllCnslTest.h"http://或?#include "DllDemo.h" 5、编译ƈ生成应用E序DllCnslTest.exe
二、显式调?/strong>
1、徏立DllWinTest工程?
2、将文gDllDemo.dll拯到DllWinTest工程所在的目录或Windowspȝ目录下?
3、用vc/bin下的Dumpbin.exe的小E序Q查看DLL文g(DllDemo.dll)中的函数l构?
4、用类型定义关键字typedefQ定义指向和DLL中相同的函数原型指针?
例: typedef int(*lpMax)(int a,int b); //此语句可以放?h文g?/p> 5、通过LoadLibray()DLL加蝲到当前的应用E序中ƈq回当前DLL文g的句柄?
例: HINSTANCE hDll; //声明一个Dll实例文g句柄 6、通过GetProcAddress()函数获取导入到应用程序中的函数指针?
例: lpMax Max; 7、函数调用完毕后Q用FreeLibrary()卸蝲DLL文g?pre> FreeLibrary(hDll); 8、编译ƈ生成应用E序DllWinTest.exe
注:昑ּ链接应用E序~译时不需要用相应的Lib文g?/p>
下蝲Q?font style="background-color: #cccccc">Visual Studio 2008验证通过Q:http://www.shnenglu.com/Files/mymsdn/DllCnsTest.7z
#pragma comment(libQ?DllDemo.lib") //在编辑器linkӞ链接到DllDemo.lib文g
extern "C"
{
DllAPI int __stdcall Max(int a,int b);
}
void main()
{
int value;
value = Max(2,9);
printf("The Max value is %d\n",value);
}
hDll = LoadLibrary("DllDemo.dll");//导入DllDemo.dll动态连接库
Max = (lpMax)GetProcAddress(hDLL,"Max");
int value;
value = Max(2,9);
printf("The Max value is %d",value);
]]>
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \止隐式构造,则可以将默认构造函数隐藏v来,在大多数~译器中也可以对构造函数增加explicit关键字来避免隐式构造?
private: \
TypeName(const TypeName&); \
TypeName& operator=(const TypeName&)
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \更多解释详见《More Effective C++?
private: \
TypeName(); \
DISALLOW_COPY_AND_ASSIGN(TypeName)
// keyword__declspec.cpp : 定义控制台应用程序的入口炏V? // // ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/kernel_d/hh/Kernel_d/64bitAMD_6db3322a-fe6d-4287-9eda-a9c1378e715d.xml.htm // The sizeof value for any structure is the offset of the final member, // plus that member's size, rounded up to the nearest multiple of the largest // member alignment value or the whole structure alignment value, // whichever is greater. #include "stdafx.h" __declspec( align( 32) ) struct Struct__declspec_1 { int a; int b; }; __declspec( align( 32) ) struct Struct__declspec_2 { __declspec( align( 64) ) int a; int b; }; __declspec( align( 8 ) ) struct Struct__declspec_3 { int a; //4 bytes int b; //4 bytes int c; //4 bytes }; __declspec( align( 8 ) ) struct Struct__declspec_4 { int a; //4 bytes int b; //4 bytes }; struct StructNormal { int a; //4 bytes int b; //4 bytes int c; //4 bytes }; int _tmain(int argc, _TCHAR* argv[]) { printf( "sizeof Struct__declspec_1 is %d.\n", sizeof( Struct__declspec_1 )); //32 printf( "sizeof Struct__declspec_2 is %d.\n", sizeof( Struct__declspec_2 )); //64 printf( "sizeof Struct__declspec_3 is %d.\n", sizeof( Struct__declspec_3 )); //16 printf( "sizeof Struct__declspec_4 is %d.\n", sizeof( Struct__declspec_4 )); //8 printf( "sizeof StructNormal is %d.\n", sizeof( StructNormal )); //12 return 0; }
其实q个时候可能是一些Vista以上版本的OS所提供的新功能引v的限制。就当前的这个例子而言Q是因ؓ~译的时候,启用用户帐户控制(UAC)默认为“是”所_解x案内所有工E选中Q右键属性,修改“配|属性?>“链接器?>“清单文件?>“启用用户帐h?UAC)”ؓ“否”,重新生成解决ҎQ即可?/p>
今晚在类中加入两个数l用来做计数Q因Z前代码有所改动QVS~译Q增量)的结果居然出C无数ơ的E序崩溃Q害我一度怀疑是不是我的数组写的有问题。囧。最后无奈之下,点了重新生成Q居焉利通过了,很生气,愤怒中?/p>
但是另外却发C一个问题,也就是当size_t用作循环的时候。因Z前都是用int做@环的Q现在换成unsigned intQ也是size_tQ后Q一下子没反应过来,顺手这么写了:
for( size_t i = MAX - 1; i >= 0; --i)
{
//…?/p>
}
乍一看似乎没啥问题,因ؓ我@环内的代码是删除资源的,因此E序也频频崩溃?/p>
step over的结果才让h惊讶Q因为当size_t i = 0的时候,--i的结果是无穷大,而无I大则肯定满i>=0的条Ӟ所以当我们期待E序停住的时候,E序是不会停住的?/p>
修正的方式:
1、用正向遍历?/p>
2、增加判断条Ӟi>=0 && i < MAXQ,但这里也可能存在问题Q因为size_t可能被定义ؓunsigned intQ但是MAX可能是个更大的数Q比如unsigned long longQ当然这L比较不是很有意义Q或者会实现一些{换,但是如果q种情况发生的话Q程序可能还是会通过一个随机的iq入C个未知的I间中,从而造成崩溃。而且增加判断条g也得程序的q行成本提高?/p>
/* * cpp_traits.cpp * * Created on: 2010-4-26 * Author: volnet@tom.com */ #include <iostream> // kinds of types for overloading. struct undefined_type {}; struct int32_type {}; struct int64_type {}; // typedef some has_trivial_* for difference types. template <class T> struct type_traits { typedef undefined_type has_trivial_type; }; // define the partial specialization functions. template <> struct type_traits<int> { typedef int32_type has_trivial_type; }; template <> struct type_traits<long> { typedef int64_type has_trivial_type; }; // the dispatcher method for all kinds of types. template <class T> void type_detect(T& p){ typedef typename type_traits<T>::has_trivial_type trivial_type; type_detect(p, trivial_type()); } // define the functions for dispatching. template <class T> void type_detect(T& p, undefined_type) { std::cout << p; std::cout << " // It's a undefined type, we have NOT found the dispatcher function." << std::endl; } template <class T> void type_detect(T& p, int32_type) { std::cout << p; std::cout << " // It's a int32" << std::endl; } template <class T> void type_detect(T& p, int64_type) { std::cout << p; std::cout << " // It's a int64" << std::endl; } int main(void) { int int32num = 2010; type_detect(int32num); long int64num = 2010L; type_detect(int64num); std::string str = "2010"; type_detect(str); std::cout << "-------end of program." << std::endl; return EXIT_SUCCESS; }
大部分h都听说过auto_ptr指针Q但是ƈ非所有h都每天用它。不使用它是不明智的Q可ȝQ,因ؓauto_ptr的设计初hZ解决C++设计和编码的普遍问题Q将它用好可以写出更健壮的代码。本文指出如何正用auto_ptr以ɽE序变得安全Q以及如何避开危险Q而不是一般用auto_ptr的恶习所致的创徏间歇性和难以诊断的问题?
auto_ptr只是许许多多指针中的一U。许多商业库提供许多更强大的指针Q可以完成更多的事情。从可以理引用计数到提供更先进的代理服务等。应该把auto_ptr认ؓ是智能指针中的福特Escort[注释]Q一个基于简单且通用目的的智能指针,既没有小发明也没有丰富的Ҏ目的更不需要高性能Q但是能许多普通的事情做好Qƈ且能够适合日常使用的智能指针?
auto_ptr做这样一件事Q拥有一个动态分配内存对象,q且在它不再需要的时候行自动清理的职责。这里有个没有用auto_ptr指针的不安全的例子:
// Example 1(a): Original code // void f() { T* pt( new T ); /*...more code...*/ delete pt; }
我们每天都像q样写代码,如果f()只是一个三行程序,也没做什么多余的事情Q这样做当然可以很好工作。但是如果f()没有执行delete语句Q比如程序提前返回(returnQ了Q或者在执行的时候抛出异怺Q然后就D已经分配的对象没有被删除Q因此我们就有了一个经典的内存泄漏?
一个ExampleQ?Q安全的办法是用一个“智能”的指针拥有q个指针Q当销毁的时候,删除那个被指的自动分配的对象。因个智能指针被单地用ؓ自动对象Q这是Q当它离开它的作用域的时候自动销毁对象)Q所以它被称作“自动”指针?
// Example 1(b): Safe code, with auto_ptr // void f() { auto_ptr<T> pt( new T ); /*...more code...*/ } // cool: pt's destructor is called as it goes out // of scope, and the object is deleted automatically
现在q段代码不会再T对象上发生泄漏了Q不必在意这个方法是正常退是异帔R出,因ؓpt的析构函数将L在堆栈弹出的时候被调用。清理工作将自动q行?/p>
最后,使用auto_ptr和用内建指针一样地ҎQ如果要“收回”资源ƈ且再ơ手动管理的话,我们可以调用release()Q?/p>
// Example 2: Using an auto_ptr // void g() { T* pt1 = new T; // right now, we own the allocated object // pass ownership to an auto_ptr auto_ptr<T> pt2( pt1 ); // use the auto_ptr the same way // we'd use a simple pointer *pt2 = 12; // same as "*pt1 = 12;" pt2->SomeFunc(); // same as "pt1->SomeFunc();" // use get() to see the pointer value assert( pt1 == pt2.get() ); // use release() to take back ownership T* pt3 = pt2.release(); // delete the object ourselves, since now // no auto_ptr owns it any more delete pt3; } // pt2 doesn't own any pointer, and so won't // try to delete it... OK, no double delete
最后,我们可以使用auto_ptr的reset()Ҏauto_ptr重置向另一个对象。如果auto_ptr已经获得一个对象,q个q程像是它先删除已l拥有的对象Q因此调用reset()Q就像是先销毁了auto_ptrQ然后重Z一个新的ƈ拥有该新对象Q?
// Example 3: Using reset() // void h() { auto_ptr<T> pt( new T(1) ); pt.reset( new T(2) ); // deletes the first T that was // allocated with "new T(1)" } // finally, pt goes out of scope and // the second T is also deleted
同样Qauto_ptr也可以被用于安全地包装指针数据成员。考虑下面使用Pimpl idiomQ或者,~译器防火墙Q的例子Q?sup>[1]
// Example 4(a): A typical Pimpl // // file c.h // class C { public: C(); ~C(); /*...*/ private: class CImpl; // forward declaration CImpl* pimpl_; }; // file c.cpp // class C::CImpl { /*...*/ }; C::C() : pimpl_( new CImpl ) { } C::~C() { delete pimpl_; }
单地_是C的私有细节被实现Z个单独的对象Q藏匿于一个指针之中。该思\要求C的构造函数负责ؓ隐藏在类内部的辅助“Pimpl”对象分配内存,q且C的析构函数负责销毁它。用auto_ptrQ我们会发现q非常容易:
// Example 4(b): A safer Pimpl, using auto_ptr // // file c.h // class C { public: C(); /*...*/ private: class CImpl; // forward declaration auto_ptr<CImpl> pimpl_; }; // file c.cpp // class C::CImpl { /*...*/ }; C::C() : pimpl_( new CImpl ) { }
现在Q析构函C需要担心删除pimpl_指针了,因ؓauto_ptr自动处理它。事实上Q如果没有其它需要显式写析构函数的原因,我们完全不需要自定义析构函数。显Ӟq比手动理指针要容易得多,q且对象所有权包含q对象是一个不错的习惯Q这正是auto_ptr所擅长的。我们将在最后再ơ回这个例子?
它本w很漂亮Qƈ且做得非常好Q从函数传入或传?span style="font-family: 'Courier New'" class="Code">auto_ptrsQ是非常有用的,比如函数的参数或者返回倹{?/p>
让我们看看ؓ什么,首先我们考虑当拷贝auto_ptr的时候会发生什么:一个auto_ptr获得一个拥有指针的对象Qƈ且在同一旉只允许有一个auto_ptr可以拥有q个对象。当你拷贝一个auto_ptr的时候,你自动将源auto_ptr的所有权Q传递给目标auto_ptrQ如果目标auto_ptr已经拥有了一个对象,q个对象先被释放。在拯完之后,只有目标auto_ptr拥有指针Qƈ且负责在合适的旉销毁它Q而源被讄为空QnullQ,q且不能再被当作原有指针的代表来使用?
例如Q?/p>
// Example 5: Transferring ownership from // one auto_ptr to another // void f() { auto_ptr<T> pt1( new T ); auto_ptr<T> pt2; pt1->DoSomething(); // OK pt2 = pt1; // now pt2 owns the pointer, // and pt1 does not pt2->DoSomething(); // OK } // as we go out of scope, pt2's destructor // deletes the pointer, but pt1's does nothing
但是要避免陷阱再ơ用已l失L有权的auto_ptrQ?
// Example 6: Never try to do work through // a non-owning auto_ptr // void f() { auto_ptr<T> pt1( new T ); auto_ptr<T> pt2; pt2 = pt1; // now pt2 owns the pointer, and // pt1 does not pt1->DoSomething(); // error! following a null pointer }
谨记于心Q我们现在看看auto_ptr如何在源和调用者之间工作。“源”这里是指一个函敎ͼ或者其它创Z个新资源的操作,q且通常移交出资源的所有权。一个“调用者”函数反转这个关p,也就是获得已l存在对象的所有权Qƈ且通常q负责释攑֮Q。而不是有一个源和调用者,q回q且利用一个秃头指针(译者注Q而不是用一个局部变量来传递这个指针)Q虽Ӟ通过一个秃头指针来获得一个资源通常很好Q?
// Example 7: Sources and sinks // // A creator function that builds a new // resource and then hands off ownership. // auto_ptr<T> Source() { return auto_ptr<T>( new T ); } // A disposal function that takes ownership // of an existing resource and frees it. // void Sink( auto_ptr<T> pt ) { } // Sample code to exercise the above: auto_ptr<T> pt( Source() ); // takes ownership
注意下面的微妙的变化Q?
Source()分配了一个新对象q且以一个完整安全的方式它q回l调用者,q让调用者成为指针的拥有着。即使调用者忽略了q回|昄Q如果调用者忽略了q回|你应该从来没有写q代码来删除q个对象Q对吧?Q,分配的对象也被自动安全地删除?/p>
在本文的最后,我将演示q回一个auto_ptr是一个好习惯。让q回值包裹进一些东西比如auto_ptr通常是得函数变得强健的有效方式?
Sink()通过传值的方式获得对象所有权。当执行完Sink()的时候,当离开作用域的时候,删除操作被执行Q只要Sink()没有所有权转移Q。上面所写的Sink()函数实际上ƈ没有对参数做M事情Q因此调用“Sink(pt);”就{于写了“pt.reset(0);”,但是大部分的Sink函数都将在释攑֮之前做一些工作?
谨记Q千万不要以我之前没有提到的方式使用auto_ptrs。我已经看见q很多程序员试着用其他方式写auto_ptrs像他们在用其它对象一栗但问题是auto_ptrq不像其他对象。这里有些基本原则,我将把它们提出来以引起你的注意:
For auto_ptr, copies are NOT equivalent.
当你试着在一般的代码中?span style="font-family: 'Courier New'" class="Code">auto_ptrs的时候,它将执行拯Qƈ且没有Q何提C,拯是不相等的(l果Q它实是拯Q。看下面q段代码Q这是我在C++新闻l经常看见的Q?
// Example 8: Danger, Will Robinson! // vector< auto_ptr<T> > v; /* ... */ sort( v.begin(), v.end() );
在标准容器中使用auto_ptrsL不安全的。一些h可能要告诉你Q他们的~译器或者类库能够很好地~译它们Q而另一些h则告诉你在某一个流行的~译器的文档中看到这个例子,不要听他们的?
问题?span style="font-family: 'Courier New'" class="Code">auto_ptrq不完全W合一个可以放q容器类型的前提Q因为拷?span style="font-family: 'Courier New'" class="Code">auto_ptrs是不{h的。首先,没有M东西说明Qvector不能军_增加q制造出“扩展”的内部拯。再ơ,当你调用一个一般函数的时候,它可能会拯元素Q就像sort()那样Q函数必L能力假设拯是等L。至一个流行的排序拯“核心”的元素Q如果你试着让它?span style="font-family: 'Courier New'" class="Code">auto_ptrs一起工作的话,它将拯一份“核心”的auto_ptr对象Q因此{UL有权q且所有权转移l一个时对象)Q然后对其余的元素也采取相同的方式(从现有成员创建更多的拥有所有权的auto_ptrQ,当排序完成后Q核心元素将被销毁,q且你将遇到一个问题:q组序列里至一个auto_ptrQ也是刚才被掉包的那个核心元素Q不再拥有对象所有权Q而那个真实的指针已经随着临时对象的销毁而被删除了!
于是标准委员会回退q希望做一些能够帮助你避免q些行ؓ的事情:标准的auto_ptr被故意设计成当你希望在用标准容器的时候用它时打断你Q或者,臛_Q在大部分的标准库实C打断你)。ؓ了达到这个目的,标准委员会利用这样一个技巧:?span style="font-family: 'Courier New'" class="Code">auto_ptr's的拷贝构造函数和赋值操作符的右|rhsQ指向非帔R。因为标准容器的单元素insert()函数Q需要一个常量作为参敎ͼ因此auto_ptrs在这里就不工作了。(译者注Q右g能赋值给非常量)
一个auto_ptr设计?span style="font-family: 'Courier New'" class="Code">const auto_ptrs不再丢失所有权Q拷贝一个const auto_ptr是违法的Q译者注Q没有这L构造函敎ͼQ实际上你可以针对它做的唯一事情是通过operator*()或者operator->()解引用它或者调用get()来获得所包含的指针的倹{这意味着我们有一个简单明了的风格来表达一个绝不丢失所有权的auto_ptrQ?
// Example 9: The const auto_ptr idiom // const auto_ptr<T> pt1( new T ); // making pt1 const guarantees that pt1 can // never be copied to another auto_ptr, and // so is guaranteed to never lose ownership auto_ptr<T> pt2( pt1 ); // illegal auto_ptr<T> pt3; pt3 = pt1; // illegal pt1.release(); // illegal pt1.reset( new T ); // illegal
q就是我要说的cosntQ因此如果现在你要向世界证明你的auto_ptr是不会被改变q且L删除其所有权Q加上const是你要做的。const auto_ptr风格是有用的Q你必须它谨记于心?
最后,auto_ptr对写出异常安全的代码有时候非常必要,思考下面的代码Q?/p>
// Example 10(a): Exception-safe? // String f() { String result; result = "some value"; cout << "some output"; return result; }
该函数有两个可见的作用:它输Z些内容,q且q回一个String。关于异常安全的详细说明出了本文的范围[2]Q但是我们想要取得的目标是强异常安全的保障Q归lؓ保函数的原子性——如果有异常Q所有的作用一起发生或者都不发生?
虽然在例10(a)中的代码非常_yQ看h相当接近于异常安全的代码Q但仍然有一些小的瑕疵,像下面的客户代码所C:
String theName; theName = f();
因ؓl果通过D回,因此String的拷贝构造函数将被调用,而拷贝赋值操作符被调用来结果拷贝到theName中。如果Q何一个拷贝失败了Qf()完成了所有它的工作以及所有它的Q务(q很好)Q但是结果是无法挽回的(哎哟我的妈呀Q?
我们可以做的更好吗,是否可以通过避免拯来避免这个问题?例如Q我们可?让函数有一个非帔R引用参数q向下面q样q回|
// Example 10(b): Better? // void f( String& result ) { cout << "some output"; result = "some value"; }
q看h很棒Q但实际不是q样的,q回result的赋值的函数只完成了一个功能,而将其它事情留给了我们。它仍然会出错。因此这个做法不可取?
解决q个问题的一个方法是q回一个指向动态分配指针的String对象Q但是最好的解决Ҏ是让我们做的更多Q返回一个指针包含在auto_ptrQ?
// Example 10(c): Correct (finally!) // auto_ptr<String> f() { auto_ptr<String> result = new String; *result = "some value"; cout << "some output"; return result; // rely on transfer of ownership; // this can't throw }
q里是一个技巧,当我们有效隐藏所有的工作来构造第二个功能Q返回|当确保它可以被安全返回给调用者ƈ且在W一个功能(打印消息Q完成的时候没有抛出操作。我们知道一旦cout完成Q返回值将成功交到调用者手中,q且无论如何都会正确清理Q如果调用者接受返回|调用者将得到q个拯的auto_ptr临时对象的所有权Q如果调用者没有接受返回|也就是忽略返回|分配的String在临时auto_ptr被销毁的时候自动清理。这U安全扩展的代h呢?像我们l常实现的强异常安全一P强安全通常消耗一些效率(通常比较)——这里指额外的动态内存分配。但是当我们在效率和正确性之间做出选择的话Q我们通常会选择后者!
让我们养成在日常工作中用auto_ptr的习惯。auto_ptr解决了常见的问题Qƈ且能够你的代码变得更安全和健壮Q特别是它可以防止内存泄漏以及确保强安全。因为它是标准的Q因此它在不同类库和q_之间是可UL的,因此无论你在哪里使用它,它都是对的?
This article is drawn from material in the new book Exceptional C++: 47 engineering puzzles, programming problems, and exception-safety solutions by Herb Sutter, © 2000 Addison Wesley Longman Inc., which contains further detailed treatments of points touched on briefly in this article, including exception safety, the Pimpl (compiler-firewall) Idiom, optimization, const-correctness, namespaces, and other C++ design and programming topics.
Pimpl风格可以有效减少目构徏旉Q因为它在CU有部分改变的时候,L客户代码引vq泛的重新编译。更多关于Pimpl风格以及如何部v~译器墙Q参考这?a href="xc++.htm">Exceptional C++的条?6?0。(Addison-Wesley, 2000Q?
See the article originally published in C++ Report and available on the Effective C++ CD (Scott Meyers, Addison-Wesley, 1999) and Items 8 to 19 in Exceptional C++ (Herb Sutter, Addison-Wesley, 2000).
Type get(int I){ return Type(i); } Type t = get(1);
q里Q?我们从C++的基本语义看上去Q?应该是Type(i) 调用一ơ拷贝构造函敎ͼ 在堆栈中生成一个时对象;然后Q用该对象构造返回对象;然后对这个时对象调用析构函敎ͼ在调用者方Q?用返回的临时对象调用拯构造函C初始化对象t, q回对象的析构函数在q之后, 函数q回之前调用?
所以, Type t = get(i); 应该有三个拷贝构造函数和两个析构函数的调?
可是Q?q有一U说法是Q?~译器可能会对这两个临时对象q行优化Q最l的优化l果会是只有一ơ的构造函数。因为很明显地可以看刎ͼ q里我们其实只是要用一个整数构造一个Type对象?
? g很有道理Q?
那么Q?哪一U说法对呢? 没有调查没有发a权,于是本h用VC++6.0做了实验?放了些cout<<?.在拷贝构造函数里Q观察打印的l果Q?l果却是跟我的simple, naïve的预一致。三个拷贝构造函敎ͼ 两个析构函数?
“你个弱智编译器Q脑袋进水了吧??忘了~译器没脑袋?“很明显在这个例子里我的两个临时对象都没有用的啊Q?
于是Q上|, 查资料, google一下吧Q?
下面是我查到的一些结果:
其实Q?q种对g递的优化的研IӞ q不只局限于q回倹{对下面q个例子Q?
void f(T t) { } void main(void){ T t1; f(t1); }
也有q种考虑?
f(T)是按g递的。语义上应该做一个复Ӟ 使得函数内部对T的改变不会媄响到原来的t1.
但是Q因为在调用f(t1)之后Q?我们没有再用t1(除了一个隐含的destructor调用)Q是否可能把复制优化掉, 直接使用t1呢?q样可以节省掉一个拷贝构造函数和一个析构函数?
可是Q?不论是对q回值的优化Q?q是对上面这U局部对象的优化Q在1995q的C++新标准草案出台前都是为标准所严格限制?(虽然有些~译器ƈ没有遵行q个标准Q?q是支持了这U“优化?
那么Q?q又是ؓ什么呢Q?
q里面涉及到一个普遍的对side-effect的担忧?
什么又是side-effect呢?
所谓side-effect是一个函数的调用与否能够对系l的状态造成区别?
int add(int i, int j){ return i+j; }是没有side-effect的,?
void set(int* p, int I, int v){ p[I]=v; }是有side-effect的。因为它改变了一个数l元素的| 而这个数l元素在函数外是可见的?
通常意义上来_ 所有的优化应该在不影响E序的可观察行ؓ的基上进行的。否则,快则快了Q?l果却和所惌的完全不同!
而C++的拷贝构造函数和析构函数又很多都是有side-effect的。如果我们的“优化”去掉了一个有side-effect的拷贝构造函数和一个析构函敎ͼ q个“优化”就有可能改变程序的可观察行为。(注意Q?我这里说的是“可能”,因ؓ“负负得正”, 两个有side-effect的函数的调用Q?在不考虑q行q行的情况下Q?也许反而不会媄响程序的可观察行为。不q, q种塞翁失马的事儿, ~译器就很难判断了)
Zq种忧虑, 1995q以前的标准Q?明确止对含有side-effect的拷贝构造函数和析构函数的优化。同Ӟ q有一些对C++扩充的提议, 考虑让程序员自己对类q行允许优化的声明?E序员可以明地告诉~译器:不错Q?我这个拷贝构造函敎ͼ 析构函数是有side-effect, 但你别管Q?管优化Q?Z事有我呢Q?
哎, side-effect真是一个让人又恨又q东西Q它使编译器的优化变得困难;加大了程序维护和调试的难度。因?functional language 把side-effect当作z水猛兽一Pq脆止。但同时Q我们又很难dside-effect. 不说E序员们更习惯于imperative 的编E方? 象数据库操作QIO操作都天然就是side-effect.
不过Q个是认为C++标准对“优化”的保守态度是有道理的。无论如何,让“优化”可以潜在地偷偷地改变程序的行ؓL让h惌v来就不舒服的?
但是Q?矛盾是对立统一的。(惛_q俺马列可得了八十多分呢Q?对这Uaggressive的“优化”的呼声是一高q一?以Stan Lippeman为首的一撮固分子Ҏ准的颠覆和和qx变的阴谋从来没有停止过?q不Q在1996q的一个风雨交加的夜晚Q?一个阴险的C++新标准草案出炉了。在q个草案里, 加入了一个名为RVO (Return Value Optimization) 的放宽对优化的限Ӟ 妄图走资本主义道路, l资本家张目的提案。其具体内容是_允许~译器对命名q的局部对象的q回q行优化Q?即拯构造函?析构函数有side-effect也在所不惜。这个提议背后所隐藏的思想是Qؓ了提高效率, 宁可冒改变程序行为的风险。宁要资本主义的苗, 不要C会M的草了!
我想Q?q样的一个罪大恶极的提案竟会被提交,应该是因为C++的值拷贝的语义的效率实在太“妈妈的”了?当你写一?Complex operator+(const Complex& c1, const Complex& c2);的时候, 竟需要调用好几次拯构造函数和析构函数Q同志们Q(沉痛圎ͼ 语重心长圎ͼC会M的生产关pȝ优越性怎么体现啊?
接下来, 当我想Google C++最新的标准Q?看RVO是否被最l采UxQ?却什么也找不C?到ANSI的网站上去, 居然要付钱才能DOWNLOAD文档?“老子在城里下馆子都不付钱Q?down你几个烂文档q要l钱Q!?
故事没有l局Q?实在是不爽?也不知是不是因ؓ标准q没有敲定, 所以VC++6 没有优化, q是VCҎ没完全遵守标准?
不过Q有一Ҏ肯定的?当写E序的时候, 最好不要依赖于RVO (有hQ?象Stan Lippeman, 又叫它NRV优化)?因ؓQ?不论Ҏ准的争论是否已经有了l果Q?实际上各个编译器的实Cq是各自为政Q?没有l一?一个叫SCOtt Meyers的家?忘了是卖什么的?pQ?如果你的E序依赖于RVO, 最好去掉这U依赖。也是_ 不管RVO到底标准不标准, 你还是不能用?不仅不能用, q得时刻警惕着RVO可能带来的程序行Z的变化?Q也不知q帮家伙瞎忙了半天到底ؓ啥!Q?
说到q里Q?倒想起了C#里一个困惑了我很久的问题。记得读C#的specification的时候, 非常不解Z么C#不允许给value type 定义析构函数?
q里Q?先简略介l一下C#里的value type (原始数据cdQ?struct cd)?
在C#里的value_typep是| 永远只能copy, 取倹{因此, 它永q是in-place的。如果你把一个value type的数据放在一个对象里Q它的生命期和那个对象相同Q如果你声明一个value type 的变量在函数中, 它的生命期就在lexical scope里?
{
The_ValueType value;
}//value 到这里就死菜?
啊呀呀Q?q不正是我们怀늚C++的stack object吗?
在C++里,Auto_ptr, shared_ptr, 容器们, 不都是利用析构函数来理资源的吗Q?
C#QJava 虽然利用garbage collection技术来攉无用对象Q?使我们不用再担心内存的回收?但garbage collectionq不保证无用对象一定被攉Q?q不保证Dispose()函数一定被调用Q?更不保证一个对象什么时候被回收?所以对一些非内存的资源, 象数据库q接Q?|络q接Q?我们q是希望能有一个类gsmart pointer的东西来帮我们管理啊。(try-finally 虽然可以用, 但因为它影响到lexical scope, 有时用v来不那么方便Q?
于是Q?我对C#的取消value type的析构函数充满了深厚的阶U仇恨?
不过Q?现在xQ?C#的这U设计一定是惩于C++p|的教训:
1. value type 没有拯构造函数。C#只做~省copy, 没有side-effect
2. value type 不准有析构函数。C#有garbage collection, 析构函数的唯一用途只会是做一些side-effect象关闭数据库q接?所以取消了析构函数Q?取消了value type的side-effect.
3. 没有了side-effect, pȝ可以L地做优化?
对以下程序:
The_Valuetype get(int I){return The_Valuetype(i);}
The_Valuetype t = get(1);
在C#里我们可以快乐地_只调用了一ơ构造函数?再没有side-effect的沙漠, 再没有难以优化的荒原Q?smart pointer望而却步, 效率之花处处开遍?I have a dream, …?/p>
转蝲自:http://gugu99.itpub.net/post/34143/466008
于是有了下面的做法Q应该不隄解:
template<class T> class smart_ptr { public: smart_ptr(T* t = 0) { real_ptr = t; } ~smart_ptr() { delete real_ptr; } T* operator ->() const { return real_ptr; } T& operator *() const { return *real_ptr; } private: T* real_ptr; };
我们希望我们的智能指针能够像指针一样地工作Q但下面的工作方式似乎存在问题:
在普通的dumb指针中,以下行ؓ是正的Q?pre class="gc-code">void letUsGo(BaseClass* objPtr); DerivedClass* derivedObjPtr2 = new DerivedClass(); letUsGo(derivedObjPtr2); delete derivedObjPtr2;
但是Q以下代码呢Q?pre class="gc-code">void letUsSmartGo(const smart_ptr<BaseClass>& objPtr); smart_ptr<DerivedClass> smartDerivedObjPtr2(new DerivedClass()); // the smart_ptr<DerivedClass> is not inherited from the smart_ptr<BaseClass> // the compiler can't find the class to cast it, so it must cause the error. letUsSmartGo(smartDerivedObjPtr2);
下面的过E描qCq个变化所需要的一些支持:
1、error C2664: “letUsSmartGo? 不能参?1 从“smart_ptr<T>”{换ؓ“const smart_ptr<T> &?br>2、smartDerivedObjPtr2的类型:
smart_ptr<DerivedClass> smartDerivedObjPtr2(new DerivedClass());
3、letUsSmartGo的声明:
void letUsSmartGo(const smart_ptr<BaseClass>&);
4、问题{化ؓQ如何从smart_ptr<DerivedClass>到const smart_ptr<BaseClass>&的{变?br>5、针对letUsSmartGo的声明,可以有的实参cd包括Q?br> const smart_ptr<BaseClass>
smart_ptr<BaseClass>
假设存在以下cd smart_derived_ptr : smart_ptr<BaseClass>Q那么smart_derived_ptr也是可以被传递的?br>6、这里存在这样一个问题:
new DerivedClass() 被传递给smartDerivedObjPtr2之后QsmartDerivedObjPtr2拥有了它的指针。如果从smartDerivedObjPtr2隐式转换成另一个smart_ptr<X>后,我们需要解决的是smartDerivedObjPtr2所拥有的指针传递给smart_ptr<X>q将smartDerivedObjPtr2的内部指针清Ӟq样׃会在smartDerivedObjPtr2被销毁的时候,因ؓ调用delete real_ptrQ而它的新拯在离开作用域的时候,一样会再次调用delete real_ptrQ而此时real_ptr指向的对象已l被释放Q因此这L行ؓ是未定义的。)
7、因此,定义如下Ҏ卛_Q?pre class="gc-code">template<class T>
class smart_ptr {
public:
smart_ptr(T* t = 0) {
std::cout << "creating smart_ptr ...smart_ptr(T* t = 0)" << std::endl;
real_ptr = t;
}
template<class U>
smart_ptr(smart_ptr<U>& rhs) : real_ptr(rhs.real_ptr){
std::cout << "creating smart_ptr ...smart_ptr(smart_ptr<U>& rhs)" << std::endl;
rhs.real_ptr = 0;
}
~smart_ptr() {
std::cout << "destoring smart_ptr ..." << std::endl;
delete real_ptr;
}
T* operator ->() const { return real_ptr; }
T& operator *() const { return *real_ptr; }
T* real_ptr;
};
8、但是这里real_ptr按照习惯应该是一个私有成员,而且我们在完成该Ҏ时候,希望能够实现一U所谓的所有权转移Q也是内部的指针传递给另一个智能指针,而这应该是一个原子过E。因此,我们实现以下ҎQ?/p>
template<class T> class smart_ptr { public: smart_ptr(T* t = 0) { std::cout << "creating smart_ptr ...smart_ptr(T* t = 0)" << std::endl; real_ptr = t; } template<class U> smart_ptr(smart_ptr<U>& rhs) : real_ptr(rhs.release()){ std::cout << "creating smart_ptr ...smart_ptr(smart_ptr<U>& rhs)" << std::endl; } ~smart_ptr() { std::cout << "destoring smart_ptr ..." << std::endl; delete real_ptr; } T* operator ->() const { return real_ptr; } T& operator *() const { return *real_ptr; } // helper T* release() { T* tmp = real_ptr; real_ptr = 0; return tmp; } private: T* real_ptr; };
1、设|“生成时启用C/C++代码分析”ؓ“是”,如果不设|此,E序速度出乎你的意料…?/p>
2、点几Z分析?>“启动性能向导?/p>
3、在“性能资源理器”中右键新徏的性能报告节点Q右键“启动ƈ启用分析功能”?/p>
虽然此处有“启用分析功能”,但如果在配置里面没有q行讄Q第一ơ的试报告l果是不准的?/p>
4、选择两个性能报告Qctrl+鼠标Q,右键“比较性能报告”?/p>
用性能报告有助于提高E序的性能Qƈ且快速定位问题所在,剩下的结果就是你自己需要多观察Q分析性能报告所反映的问题了?br>
更多
VC++ 6.0详见q里>> http://neural.cs.nthu.edu.tw/jang/mir/technicalDocument/vc6_profile/index.htm
W三方工?gt;> http://www.semdesigns.com/Products/Profilers/CppProfiler.html
/* * single_argument_ctor.cpp * * Created on: 2010-3-29 * Author: Volnet * Compiler: GNU C++(version 3.4.5) * MSVC CL(version 15.00.30729.01) */ #include <stdlib.h> #include <iostream> #include <sstream> // single-argument class class SingleArgumentClass { private: int _inner; public: SingleArgumentClass() :_inner(-1) { } SingleArgumentClass(int actual) :_inner(actual) { } bool operator==(const SingleArgumentClass& rhs); std::string str(){ // we'd better to use boost::lexical_cast to cast. // #region cast std::stringstream strStream; strStream << _inner; std::string str; strStream >> str; // #endregion return str; } }; bool SingleArgumentClass::operator ==(const SingleArgumentClass& rhs){ if(_inner == rhs._inner) return true; return false; } // single-argument class fixed bug by explicit keyword. class SingleArgumentClassFixedBugByExplicitKeyword { private: int _inner; public: SingleArgumentClassFixedBugByExplicitKeyword() :_inner(-1) { } explicit SingleArgumentClassFixedBugByExplicitKeyword(int actual) :_inner(actual) { } bool operator==(const SingleArgumentClassFixedBugByExplicitKeyword& rhs); std::string str(){ // we'd better to use boost::lexical_cast to cast. // #region cast std::stringstream strStream; strStream << _inner; std::string str; strStream >> str; // #endregion return str; } }; bool SingleArgumentClassFixedBugByExplicitKeyword::operator ==(const SingleArgumentClassFixedBugByExplicitKeyword& rhs){ if(_inner == rhs._inner) return true; return false; } // single-argument class fixed bug by helper class. class ActualType { public: ActualType(int value):_value(value){}; int get_value(){ return _value; } private: int _value; }; class SingleArgumentClassFixedBugByHelperClass { private: int _inner; public: SingleArgumentClassFixedBugByHelperClass() :_inner(-1) { } SingleArgumentClassFixedBugByHelperClass(ActualType actual) :_inner(actual.get_value()) { } bool operator==(const SingleArgumentClassFixedBugByHelperClass& rhs); std::string str(){ // we'd better to use boost::lexical_cast to cast. // #region cast std::stringstream strStream; strStream << _inner; std::string str; strStream >> str; // #endregion return str; } }; bool SingleArgumentClassFixedBugByHelperClass::operator ==(const SingleArgumentClassFixedBugByHelperClass& rhs){ if(_inner == rhs._inner) return true; return false; } void Assert(bool status, std::string strTrue = std::string("assert result is true;"), std::string strFalse = std::string("assert result is false;")); int main(void){ SingleArgumentClass obj(3); std::cout << obj.str() << std::endl; // our purpose. SingleArgumentClass obj1(1); SingleArgumentClass obj2(1); Assert(obj1 == obj2, "obj1 == obj2", "obj1 != obj2"); int i = 3; // warning!!! // obj is a SingleArgumentClass object. // i is a integer. // operator== only define the equal between two SingleArgumentClass object. // In fact: // obj == i: // 1.compiler found the operator== require two SingleArgumentClass object. // 2.compiler try to find a cast method for casting int to SingleArgumentClass. // 3.compiler found it can use the SingleArguementClass.Ctor(int) // to create a new SingleArgumentClass. // 4.compiler try to create a new SingleArgumentClass object from i. // 5.so it without any warning and error, but it's logical not we need. Assert(obj == i, "obj == i //right?", "obj != i"); // Assert(i == obj); // Compile ERROR: no match for 'operator==' in 'i == obj' // it's may encounter a compile-time error. // GNU G++: no match for 'operator==' in 'objFixed == i' single_argument_ctor.cpp single_argument_ctor/src 106 C/C++ Problem // MSVC: 错误 1 error C2679: 二进制?=? 没有扑ֈ接受“int”类型的x作数的运符(或没有可接受的{? {projectpath}\single_argument_ctor\src\single_argument_ctor.cpp 107 single_argument_ctor SingleArgumentClassFixedBugByExplicitKeyword objFixed(3); // Assert(objFixed == i, "objFixed == i", "objFixed != i"); SingleArgumentClassFixedBugByHelperClass objFixedByHelper1(3); SingleArgumentClassFixedBugByHelperClass objFixedByHelper2(3); // it's may encounter a compile-time error. // GNU G++: no match for 'operator==' in 'objFixedAuto1 == i' single_argument_ctor.cpp single_argument_ctor/src 158 C/C++ Problem // MSVC: 错误 1 error C2679: 二进制?=? 没有扑ֈ接受“int”类型的x作数的运符(或没有可接受的{? {projectpath}\single_argument_ctor\src\single_argument_ctor.cpp 163 single_argument_ctor // Assert(objFixedByHelper1 == i); } void Assert(bool status, std::string strTrue, std::string strFalse) { std::cout << (status strTrue : strFalse) << std::endl; }
解决ҎQ?/p>
1、explicit关键字,在单参数构造函数前使用explicit参数Q可以避免单参数构造函数被用于隐式转换?/p>
2、利用中间类的方式,详见代码“SingleArgumentClassFixedBugByHelperClass相关部分”。如果编译器不支持解x?Q则使用此方法。上面代码所提及的两ƾ主编译器均支持explicit关键字?/p>
有时候我们可以用namespace来组l命名空间?/p>
有时候我们又希望一些较深层ơ的cd成我们比较容易访问的对象?/p>
下面的代码提供了一U简单的CZ来满L需求?/p>
1、用namespace来组l各个类的层U关pR?/p>
2、用using关键字,深层次l构暴露到较外层?/p>
//============================================================================ // Name : namespace.cpp // Author : Volnet // Version : // Copyright : reserve by volnet@tom.com // Description : namespace in C++, Ansi-style //============================================================================ #include <iostream> namespace volnet { namespace extensions { class _Console { public: void WriteLine(std::string); }; } using extensions::_Console; } using namespace volnet; _Console Console; void _Console::WriteLine(std::string s) { std::cout << s << std::endl; } using namespace std; int main() { Console.WriteLine(std::string("I'm volnet!")); return 0; }
boost::tuple<derived> tup4; boost::tuple<base> tup5; tup5 = tup4; tup4.get<0>().test(); tup5.get<0>().test(); // 丢失多态?br> derived d; boost::tuple<derived*> tup6(&d); boost::tuple<base*> tup7; tup7 = tup6; tup6.get<0>()->test(); tup7.get<0>()->test(); // 恢复多态性(Ҏ1Q? boost::tuple<derived&> tup8(d); boost::tuple<base&> tup9(tup8);
// tup9 = tup8; 不能使用该方法,因ؓ无法对引用赋倹{? tup8.get<0>().test(); tup9.get<0>().test(); // 恢复多态性(Ҏ2Q?/span>
/* * tuple.cpp * * Created on: 2010-3-25 * Author: GoCool */ #include <stdlib.h> #include <iostream> #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_io.hpp> #include "../header/baseClass.h" using namespace std; class X { X(); public: X(std::string){} }; class Y { Y(const Y&); public: Y(){} }; class A { }; bool operator==(A, A) { std::cout << "All the same to me..."; return true; } void f(int i); void cut_off_rule(void); int main(void){ // add a new tuple boost::tuple<int,double,std::string> triple(42, 3.14, "My first tuple!"); int a = triple.get<0>(); ++a; cout << a << endl; cout << triple << endl; cut_off_rule(); boost::tuple<int, double> pair = boost::make_tuple(21, 22.5); cout << pair << endl; cut_off_rule(); int pair_element_1 = -1; double pair_element_2 = -1; boost::tie(pair_element_1, pair_element_2) = pair; cout << pair_element_1 << "," << pair_element_2 << endl; cut_off_rule(); boost::tuple<int,std::string,derived> tup1(-5,"Tuples"); boost::tuple<unsigned int,std::string,base> tup2; tup2=tup1; tup2.get<2>().test(); std::cout << "Interesting value: " << tup2.get<0>() << '\n'; const boost::tuple<double,std::string,base> tup3(tup2); // Description Resource Path Location Type // assignment of read-only location tuple.cpp boost_tuple/src 45 C/C++ Problem // tup3.get<0>()=3.14; cut_off_rule(); boost::tuple<X,X,X> obj = boost::tuple<X,X,X>(string("Jaba"), string("Daba"), string("Duu")); // ok cut_off_rule(); double dNum = 5; boost::tuple<double&> numTuple(dNum); // ok // boost::tuple<double&>(dNum+3.14); // error: cannot initialize // non-const reference with a temporary boost::tuple<const double&>(dNum+3.14); // ok, but dangerous: // the element becomes a dangling reference cut_off_rule(); // char arr[2] = {'a', 'b'}; // boost::tuple<char[2]>(arr); // error, arrays can not be copied // boost::tuple<char[2], Y>(arr, Y()); // error, neither arrays nor Y can be copied boost::tuple<char[2], Y>(); // ok cut_off_rule(); boost::tuple<void (*)(int)> pFTuple1 = boost::make_tuple(&f); pFTuple1.get<0>()(10); boost::tuple<void (*)(int)> pFTuple2 = boost::make_tuple(boost::ref(f)); pFTuple2.get<0>()(20); boost::tuple<void (&)(int)> pFTuple3(f); pFTuple3.get<0>()(30); boost::tuple<boost::tuple<void (&)(int)> > pFTuple4(f); pFTuple4.get<0>().get<0>()(40); cut_off_rule(); // boost::tuple<int, char> stdPairToTuple = std::make_pair(1, 'a'); cut_off_rule(); boost::tuple<std::string, int, A> t1(std::string("same?"), 2, A()); boost::tuple<std::string, long> t2(std::string("same?"), 2); boost::tuple<std::string, long> t3(std::string("different"), 3); // t1 == t2; // true cut_off_rule(); int i; char c; boost::tie(i, c) = std::make_pair(1, 'a'); cout << i << " " << c << endl; cut_off_rule(); boost::tie(boost::tuples::ignore, c) = std::make_pair(1, 'a'); cout << c << endl; cut_off_rule(); int myX = -1; double myY = -2; boost::tuple<int, double> f2(2); boost::tie(myX, myY) = f2; // #2 cout << "myX = " << myX << ", myY = " <<myY << endl; } void cut_off_rule(void) { cout << "-----------------------------------" << endl; } void f(int i) { cout << "f(" << i << ")" << endl; }
tuple是boost库中一个类似标准std::pair库库Q但pair只能支持两种元素Q而tuple则可以支持大于两U的?/p>
更多详解Q?a title="http://www.boost.org/doc/libs/1_42_0/libs/tuple/doc/tuple_users_guide.html" >http://www.boost.org/doc/libs/1_42_0/libs/tuple/doc/tuple_users_guide.html
以下内容直接引自原文Q?/p>
?..one of the most highly regarded and expertly designed C++ library projects in the world.??Herb Sutter and Andrei Alexandrescu, C++ Coding Standards
A tuple (or n-tuple) is a fixed size collection of elements. Pairs, triples, quadruples etc. are tuples. In a programming language, a tuple is a data object containing other objects as elements. These element objects may be of different types.
Tuples are convenient in many circumstances. For instance, tuples make it easy to define functions that return more than one value.
Some programming languages, such as ML, Python and Haskell, have built-in tuple constructs. Unfortunately C++ does not. To compensate for this "deficiency", the Boost Tuple Library implements a tuple construct using templates.
Advanced features (describes some metafunctions etc.).
Rationale behind some design/implementation decisions.
To use the library, just include:
#include "boost/tuple/tuple.hpp"
Comparison operators can be included with:
#include "boost/tuple/tuple_comparison.hpp"
To use tuple input and output operators,
#include "boost/tuple/tuple_io.hpp"
Both tuple_io.hpp
and tuple_comparison.hpp
include tuple.hpp
.
All definitions are in namespace ::boost::tuples
, but the most common names are lifted to namespace ::boost
with using declarations. These names are: tuple
, make_tuple
, tie
and get
. Further, ref
and cref
are defined directly under the ::boost
namespace.
A tuple type is an instantiation of the tuple
template. The template parameters specify the types of the tuple elements. The current version supports tuples with 0-10 elements. If necessary, the upper limit can be increased up to, say, a few dozen elements. The data element can be any C++ type. Note that void
and plain function types are valid C++ types, but objects of such types cannot exist. Hence, if a tuple type contains such types as elements, the tuple type can exist, but not an object of that type. There are natural limitations for element types that cannot be copied, or that are not default constructible (see 'Constructing tuples' below).
For example, the following definitions are valid tuple instantiations (A
, B
and C
are some user defined classes):
tuple<int>
tuple<double&, const double&, const double, double*, const double*>
tuple<A, int(*)(char, int), B(A::*)(C&), C>
tuple<std::string, std::pair<A, B> >
tuple<A*, tuple<const A*, const B&, C>, bool, void*>
The tuple constructor takes the tuple elements as arguments. For an n-element tuple, the constructor can be invoked with k arguments, where 0 <= k <= n. For example:
tuple<int, double>()
tuple<int, double>(1)
tuple<int, double>(1, 3.14)
If no initial value for an element is provided, it is default initialized (and hence must be default initializable). For example.
class X {
X();
public:
X(std::string);
};
tuple<X,X,X>() // error: no default constructor for X
tuple<X,X,X>(string("Jaba"), string("Daba"), string("Duu")) // ok
In particular, reference types do not have a default initialization:
tuple<double&>() // error: reference must be
// initialized explicitly
double d = 5;
tuple<double&>(d) // ok
tuple<double&>(d+3.14) // error: cannot initialize
// non-const reference with a temporary
tuple<const double&>(d+3.14) // ok, but dangerous:
// the element becomes a dangling reference
Using an initial value for an element that cannot be copied, is a compile time error:
class Y {
Y(const Y&);
public:
Y();
};
char a[10];
tuple<char[10], Y>(a, Y()); // error, neither arrays nor Y can be copied
tuple<char[10], Y>(); // ok
Note particularly that the following is perfectly ok:
Y y;
tuple<char(&)[10], Y&>(a, y);
It is possible to come up with a tuple type that cannot be constructed. This occurs if an element that cannot be initialized has a lower index than an element that requires initialization. For example: tuple<char[10], int&>
.
In sum, the tuple construction is semantically just a group of individual elementary constructions.
make_tuple
functionTuples can also be constructed using the make_tuple
(cf. std::make_pair
) helper functions. This makes the construction more convenient, saving the programmer from explicitly specifying the element types:
tuple<int, int, double> add_multiply_divide(int a, int b) {
return make_tuple(a+b, a*b, double(a)/double(b));
}
By default, the element types are deduced to the plain non-reference types. E.g.:
void foo(const A& a, B& b) {
...
make_tuple(a, b);
The make_tuple
invocation results in a tuple of type tuple<A, B>
.
Sometimes the plain non-reference type is not desired, e.g. if the element type cannot be copied. Therefore, the programmer can control the type deduction and state that a reference to const or reference to non-const type should be used as the element type instead. This is accomplished with two helper template functions: ref
and cref
. Any argument can be wrapped with these functions to get the desired type. The mechanism does not compromise const correctness since a const object wrapped with ref
results in a tuple element with const reference type (see the fifth example below). For example:
A a; B b; const A ca = a;
make_tuple(cref(a), b); // creates tuple<const A&, B>
make_tuple(ref(a), b); // creates tuple<A&, B>
make_tuple(ref(a), cref(b)); // creates tuple<A&, const B&>
make_tuple(cref(ca)); // creates tuple<const A&>
make_tuple(ref(ca)); // creates tuple<const A&>
Array arguments to make_tuple
functions are deduced to reference to const types by default; there is no need to wrap them with cref
. For example:
make_tuple("Donald", "Daisy");
This creates an object of type tuple<const char (&)[7], const char (&)[6]>
(note that the type of a string literal is an array of const characters, not const char*
). However, to get make_tuple
to create a tuple with an element of a non-const array type one must use the ref
wrapper.
Function pointers are deduced to the plain non-reference type, that is, to plain function pointer. A tuple can also hold a reference to a function, but such a tuple cannot be constructed with make_tuple
(a const qualified function type would result, which is illegal):
void f(int i);
...
make_tuple(&f); // tuple<void (*)(int)>
...
volnet:
boost::tuple<void (&)(int)> pFTuple3(f);
pFTuple3.get<0>()(30);
tuple<tuple<void (&)(int)> > a(f) // ok
make_tuple(f); // not ok
Tuple elements are accessed with the expression:
t.get<N>()
or
get<N>(t)
where t
is a tuple object and N
is a constant integral expression specifying the index of the element to be accessed. Depending on whether t
is const or not, get
returns the N
th element as a reference to const or non-const type. The index of the first element is 0 and thus N
must be between 0 and k-1
, where k
is the number of elements in the tuple. Violations of these constraints are detected at compile time. Examples:
double d = 2.7; A a;
tuple<int, double&, const A&> t(1, d, a);
const tuple<int, double&, const A&> ct = t;
...
int i = get<0>(t); i = t.get<0>(); // ok
int j = get<0>(ct); // ok
get<0>(t) = 5; // ok
get<0>(ct) = 5; // error, can't assign to const
...
double e = get<1>(t); // ok
get<1>(t) = 3.14; // ok
get<2>(t) = A(); // error, can't assign to const
A aa = get<3>(t); // error: index out of bounds
...
++get<0>(t); // ok, can be used as any variable
Note! The member get functions are not supported with MS Visual C++ compiler. Further, the compiler has trouble with finding the non-member get functions without an explicit namespace qualifier. Hence, all get
calls should be qualified as: tuples::get<N>(a_tuple)
when writing code that should compile with MSVC++ 6.0.
A tuple can be copy constructed from another tuple, provided that the element types are element-wise copy constructible. Analogously, a tuple can be assigned to another tuple, provided that the element types are element-wise assignable. For example:
class A {};
class B : public A {};
struct C { C(); C(const B&); };
struct D { operator C() const; };
tuple<char, B*, B, D> t;
...
tuple<int, A*, C, C> a(t); // ok
a = t; // ok
In both cases, the conversions performed are: char -> int
, B* -> A*
(derived class pointer to base class pointer), B -> C
(a user defined conversion) and D -> C
(a user defined conversion).
Note that assignment is also defined from std::pair
types:
tuple<float, int> a = std::make_pair(1, 'a');
volnet:(Eclipse with MinGW g++
conversion from `std::pair<int, char>' to non-scalar type `boost::tuples::tuple<float, int, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type>' requested
Tuples reduce the operators ==, !=, <, >, <=
and >=
to the corresponding elementary operators. This means, that if any of these operators is defined between all elements of two tuples, then the same operator is defined between the tuples as well. The equality operators for two tuples a
and b
are defined as:
a == b
iff for each i
: ai == bi
a != b
iff exists i
: ai != bi
The operators <, >, <=
and >=
implement a lexicographical ordering.
Note that an attempt to compare two tuples of different lengths results in a compile time error. Also, the comparison operators are "short-circuited": elementary comparisons start from the first elements and are performed only until the result is clear.
Examples:
tuple<std::string, int, A> t1(std::string("same?"), 2, A());
tuple<std::string, long, A> t2(std::string("same?"), 2, A());
tuple<std::string, long, A> t3(std::string("different"), 3, A());
bool operator==(A, A) { std::cout << "All the same to me..."; return true; }
t1 == t2; // true
t1 == t3; // false, does not print "All the..."
Tiers are tuples, where all elements are of non-const reference types. They are constructed with a call to the tie
function template (cf. make_tuple
):
int i; char c; double d;
...
tie(i, c, a);
The above tie
function creates a tuple of type tuple<int&, char&, double&>
. The same result could be achieved with the call make_tuple(ref(i), ref(c), ref(a))
.
A tuple that contains non-const references as elements can be used to 'unpack' another tuple into variables. E.g.:
int i; char c; double d;
tie(i, c, d) = make_tuple(1,'a', 5.5);
std::cout << i << " " << c << " " << d;
This code prints 1 a 5.5
to the standard output stream. A tuple unpacking operation like this is found for example in ML and Python. It is convenient when calling functions which return tuples.
The tying mechanism works with std::pair
templates as well:
int i; char c;
tie(i, c) = std::make_pair(1, 'a');
There is also an object called ignore
which allows you to ignore an element assigned by a tuple. The idea is that a function may return a tuple, only part of which you are interested in. For example (note, that ignore
is under the tuples
subnamespace):
char c;
tie(tuples::ignore, c) = std::make_pair(1, 'a');
The global operator<<
has been overloaded for std::ostream
such that tuples are output by recursively calling operator<<
for each element.
Analogously, the global operator>>
has been overloaded to extract tuples from std::istream
by recursively calling operator>>
for each element.
The default delimiter between the elements is space, and the tuple is enclosed in parenthesis. For Example:
tuple<float, int, std::string> a(1.0f, 2, std::string("Howdy folks!");
cout << a;
outputs the tuple as: (1.0 2 Howdy folks!)
The library defines three manipulators for changing the default behavior:
set_open(char)
defines the character that is output before the first element.
set_close(char)
defines the character that is output after the last element.
set_delimiter(char)
defines the delimiter character between elements.Note, that these manipulators are defined in the tuples
subnamespace. For example:
cout << tuples::set_open('[') << tuples::set_close(']') << tuples::set_delimiter(',') << a;
outputs the same tuple a
as: [1.0,2,Howdy folks!]
The same manipulators work with operator>>
and istream
as well. Suppose the cin
stream contains the following data:
(1 2 3) [4:5]
The code:
tuple<int, int, int> i;
tuple<int, int> j;
cin >> i;
cin >> tuples::set_open('[') >> tuples::set_close(']') >> tuples::set_delimiter(':');
cin >> j;
reads the data into the tuples i
and j
.
Note that extracting tuples with std::string
or C-style string elements does not generally work, since the streamed tuple representation may not be unambiguously parseable.
All tuple access and construction functions are small inlined one-liners. Therefore, a decent compiler can eliminate any extra cost of using tuples compared to using hand-written tuple like classes. Particularly, with a decent compiler there is no performance difference between this code:
class hand_made_tuple {
A a; B b; C c;
public:
hand_made_tuple(const A& aa, const B& bb, const C& cc)
: a(aa), b(bb), c(cc) {};
A& getA() { return a; };
B& getB() { return b; };
C& getC() { return c; };
};
hand_made_tuple hmt(A(), B(), C());
hmt.getA(); hmt.getB(); hmt.getC();
and this code:
tuple<A, B, C> t(A(), B(), C());
t.get<0>(); t.get<1>(); t.get<2>();
Note, that there are widely used compilers (e.g. bcc 5.5.1) which fail to optimize this kind of tuple usage.
Depending on the optimizing ability of the compiler, the tier mechanism may have a small performance penalty compared to using non-const reference parameters as a mechanism for returning multiple values from a function. For example, suppose that the following functions f1
and f2
have equivalent functionalities:
void f1(int&, double&);
tuple<int, double> f2();
Then, the call #1 may be slightly faster than #2 in the code below:
int i; double d;
...
f1(i,d); // #1
tie(i,d) = f2(); // #2
volnet:
int myX = -1;
double myY = -2;
boost::tuple<int, double> f2(2);
boost::tie(myX, myY) = f2; // #2
cout << "myX = " << myX << ", myY = " <<myY << endl;
See [1, 2] for more in-depth discussions about efficiency.
Compiling tuples can be slow due to the excessive amount of template instantiations. Depending on the compiler and the tuple length, it may be more than 10 times slower to compile a tuple construct, compared to compiling an equivalent explicitly written class, such as the hand_made_tuple
class above. However, as a realistic program is likely to contain a lot of code in addition to tuple definitions, the difference is probably unnoticeable. Compile time increases between 5 and 10 percent were measured for programs which used tuples very frequently. With the same test programs, memory consumption of compiling increased between 22% to 27%. See [1, 2] for details.
The library code is(?) standard C++ and thus the library works with a standard conforming compiler. Below is a list of compilers and known problems with each compiler:
Compiler
Problems
gcc 2.95
-
edg 2.44
-
Borland 5.5
Can't use function pointers or member pointers as tuple elements
Metrowerks 6.2
Can't use ref
and cref
wrappers
MS Visual C++
No reference elements (tie
still works). Can't use ref
and cref
wrappers
Gary Powell has been an indispensable helping hand. In particular, stream manipulators for tuples were his idea. Doug Gregor came up with a working version for MSVC, David Abrahams found a way to get rid of most of the restrictions for compilers not supporting partial specialization. Thanks to Jeremy Siek, William Kempf and Jens Maurer for their help and suggestions. The comments by Vesa Karvonen, John Max Skaller, Ed Brey, Beman Dawes, David Abrahams and Hartmut Kaiser helped to improve the library. The idea for the tie mechanism came from an old usenet article by Ian McCulloch, where he proposed something similar for std::pairs.
[1] Järvi J.: Tuples and multiple return values in C++, TUCS Technical Report No 249, 1999.
[2] Järvi J.: ML-Style Tuple Assignment in Standard C++ - Extending the Multiple Return Value Formalism, TUCS Technical Report No 267, 1999.
[3] Järvi J.:Tuple Types and Multiple Return Values, C/C++ Users Journal, August 2001.
Last modified 2003-09-07
© Copyright Jaakko Järvi 2001. Permission to copy, use, modify, sell and distribute this software and its documentation is granted provided this copyright notice appears in all copies. This software and its documentation is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.
原文链接Q?a href="http://www.shnenglu.com/mymsdn/archive/2009/03/06/quicksort.aspx">http://www.shnenglu.com/mymsdn/archive/2009/03/06/quicksort.aspx Q未修复Q?/p>
关于修复BUG后的代码Q将在本文中提供Q原文中不进行修改,但会有提C指明是错误的,有兴的朋友也可以直接在原文的代码中L错误来锻D己排错的能力?/p>
下面是几段代码Q?/p>
Algorithms.cpp
#include "StdAfx.h" #include "Algorithms.h" Algorithms::Algorithms(void) { } Algorithms::~Algorithms(void) { }
Algorithms.h
#pragma once #include <iostream> class Algorithms { public: Algorithms(void); ~Algorithms(void); public: template <typename T> static void QuickSort(T* arr, size_t min, size_t max); private: template <typename T> static size_t qsort_helper_partition(T* arr, size_t min, size_t max); template <typename T> static inline void swap(T* arr, size_t x, size_t y); // helper template <typename T> static inline void helper_show_all(T* arr, size_t min, size_t max, char *msg); }; template <typename T> void Algorithms::QuickSort(T* arr, size_t min, size_t max) { if(min >= max || max == 0 - 1) return; helper_show_all(arr, min, max, "before qsort_helper_partition"); size_t p = qsort_helper_partition(arr, min, max); helper_show_all(arr, min, max, "after qsort_helper_partition"); QuickSort(arr, min, p - 1); QuickSort(arr, p + 1, max); } /* * @BUG: bug200910280001 * @DESC: ׃在@环while(true)中,假设原代? 01 while(true) 02 { 03 while(cmp < arr[i]) 04 ++i; 05 while(arr[j] < cmp) 06 --j; 07 if(i >= j) break; 08 09 swap(arr, i, j); 10 } 中,前两D(行号Q行P中的代码均返回falseQ? 则无法进?+i或?-jQ那么在q个while(true)中, i和j的值将无法发生变化Q从而导致死循环? @LINK:http://www.shnenglu.com/mymsdn/archive/2009/03/06/quicksort.aspx#99606 */ template <typename T> size_t Algorithms::qsort_helper_partition(T* arr, size_t min, size_t max) { T cmp = arr[min]; int i = min, j = max; // bug200910280001:修正i = min+1Q将+1的动作放在@环内? while(true) { while(cmp < arr[++i]) // bug200910280001:原本在循环外的min+1Q移q@环内Qƈ首先+1 ; // bug200910280001:?+1U至while条g中? while(arr[j] < cmp) --j; if(i >= j) break; helper_show_all(arr, min, max, "before swap(arr, i, j)"); swap(arr, i, j); helper_show_all(arr, min, max, "after swap(arr, i, j)"); } swap(arr, min, j); return j; } template <typename T> void Algorithms::swap(T* arr, size_t x, size_t y) { T tmp = arr[x]; arr[x] = arr[y]; arr[y] = tmp; } template <typename T> void Algorithms::helper_show_all(T* arr, size_t min, size_t max, char *msg) { std::cout << "current array :\t"; for(int i = min; i < max; ++i) { std::cout << arr[i] << " "; } std::cout<<"\t//"<<msg; std::cout<<std::endl; }
cpp_quickSort.cpp
// cpp_quickSort.cpp : 定义控制台应用程序的入口炏V? // #include "stdafx.h" #include "Algorithms.h" #include <iostream> #include <vector> #include <algorithm> int _tmain(int argc, _TCHAR* argv[]) { int arr_begin = 0; int arr_length = 12; //The length will instead of the magic numbers int arr[] = {8, 3, 7, 1, 5, 6, 2, 1, 9, 9, 1, 1}; // Test for : 20091028bug0001 // int arr[] = {1, 1, 1}; std::cout << "input array :\t"; for(size_t i = arr_begin; i != arr_length; ++i) { std::cout<<arr[i]<<" "; } std::cout<<std::endl; Algorithms::QuickSort(arr, arr_begin, arr_length); std::cout << "result array :\t"; for(size_t i = arr_begin; i != arr_length; ++i) { std::cout<<arr[i]<<" "; } std::cout<<std::endl; std::cout << "--------------------" << std::endl; std::cout << "input array :\t"; std::vector<int> vec; vec.push_back(3); vec.push_back(1); vec.push_back(4); vec.push_back(1); vec.push_back(7); vec.push_back(6); for(std::vector<int>::iterator iter = vec.begin(); iter != vec.end(); ++ iter) { std::cout<<*iter<<" "; } std::cout<<std::endl; std::sort(vec.begin(), vec.end()); for(std::vector<int>::iterator iter = vec.begin(); iter != vec.end(); ++ iter) { std::cout<<*iter<<" "; } std::cout<<std::endl; return 0; }
另外Q附C FAQ上关于null pointer的解释:C FAQQnull pointer
原理Q因??0ơ方是1MBQ??0ơ方?KBQ??0ơ方?GBQ以此类推)?/p>
如果你请求分配的内存块小?MBQ你得到的内存是否比q要多一些呢Qؓ什么?
{:
q不是绝对的Q?/p>
在本例中使用
1<<22Q?MBQ得到的l果??000MB
1<<21Q?MBQ得到的l果??972MB
1<<20Q?MBQ得到的l果?918MB
1<<19Q?.5MBQ得到的l果??812MB
1<<18Q?.25MBQ得到的l果??003MB
1<<17Q?.125MBQ得到的l果??034MB
昄出现了一个意外的l果?/p>
#include <stdio.h> #include <stdlib.h> char * favorite_fruit1(void); char * favorite_fruit2(void); void favorite_fruit3(char **); int main(void) { char * fruit1 = favorite_fruit1(); printf("%s\n", fruit1); char * fruit2 = favorite_fruit2(); printf("%s\n", fruit2); char * fruit3 = NULL; favorite_fruit3(&fruit3); printf("%s\n", fruit3); printf("------END of CODE------"); return EXIT_SUCCESS; } char * favorite_fruit1(void){ char deciduous[] = "apple"; return deciduous; } char * favorite_fruit2(void){ static char deciduous[] = "apple"; return deciduous; } void favorite_fruit3(char ** fruit){ static char deciduous[] = "apple"; *fruit = deciduous; }
favorite_fruit1很明显会出现问题Q原因是因ؓchar deciduous[]是局部变量,在函数调用返回后Q就释放了?/p>
favorite_fruit2因ؓ使用了staticQ而static限定了变量被保存在数据段Qdata segmentQ中Q它的声明周期同E序一样长。所以不会出错?/p>
favorite_fruit3是另一U有效的写法Q其原理??/p>
2、早期的gets()中的BugD了Internet蠕虫QP42Q?/p>
gets()函数q不查缓冲区的空_事实上它也无法检查缓冲区的空间。如果函数的调用者提供了一个指向堆栈的指针Qƈ且gets()函数d的字W数量超q缓冲区的空_gets()函数会愉快地将多出来的字符l箋写入到堆栈中Q这p盖了堆栈原先的内宏V——这是病毒利用它来写入额外I间Qƈ引发蠕虫病毒的前提?/p>
推荐的方式是?/p>
gets(line)
替换?/p>
if(fgets(line, sizeof(line), stdin) == NULL)
exit(1);
3、相dW串帔R自动q接QP45Q?/p>
q个其实已经应用很普遍了Q但是我个h用的比较,Ҏ记录一下?/p>
4、返回一个指针?QP48Q?/p>
q个话题围绕一个程序的BUG来展开Q这个程序返回了局部变量的值的指针Q这么说当然你一眼就能看得出来问题所在,但是在很多时候,q个错误却L在你的眼皮子底下溜走?/p>
作者提供了五种方式Q只能说可以用,但唯一推荐的只有一个,详见作者的分析QP48Q(不是什么高q理论Q你自己也能分析地出来)?/p>
a.q回一个字W串帔R的指针。因为常量存在静态数据存储区Q所以指针没问题?/p>
b.使用全局声明的数l。提到全局两个字,q道这个方法有很大的局限性?/p>
c.使用静态数l。下一ơ调用将覆盖q个数组内容?/p>
char * func() {
static char buffer[20];
?/p>
return buffer;
}
d.昑ּ分配一些内存,保存q回的倹{?/p>
char * func() {
char * s = malloc(120);
?/p>
return s;
}
既然用到了mallocQ就必然伴随着freeQ因此带来了内存理的问题,增加了开发者负担?/p>
e.Q?u>推荐Q在调用前后Q由函数调用者分配内存,q由光放,在同一地方释放对于内存理来说代h相对最?/p>
void func( char * result, int size) {
?/p>
strncpy(result, “That’d be in the data segment, Bob? size);
}
buffer = malloc(size);
func(buffer, size);
?/p>
free(buffer);
本文代码已经更新Q修正严重BUGQ最新版本详见?a href="http://www.shnenglu.com/mymsdn/archive/2009/10/28/quicksort20091028.html">QuickSort快速排序法(2009-10-28)》!
本文中所涉及的代码未做更斎ͼL步最新版查阅正确代码Q?/p>
链接Q?a title="http://www.shnenglu.com/mymsdn/archive/2009/10/28/quicksort20091028.html" href="http://www.shnenglu.com/mymsdn/archive/2009/10/28/quicksort20091028.html">http://www.shnenglu.com/mymsdn/archive/2009/10/28/quicksort20091028.html
快速排序法Q(好土Q感觉满世界都会Q不q还是写一下,当然了,标准库里多的是排序算法)Q这里还是实现经典版的快速排序了Q时间复杂度O(nlogn)
Algorithms.h
#pragma once #include <iostream> class Algorithms { public: Algorithms(void); ~Algorithms(void); public: template <typename T> static void QuickSort(T* arr, size_t min, size_t max); private: template <typename T> static size_t qsort_helper_partition(T* arr, size_t min, size_t max); template <typename T> static inline void swap(T* arr, size_t x, size_t y); }; template <typename T> void Algorithms::QuickSort(T* arr, size_t min, size_t max) { if(min >= max || max == 0 - 1) return; size_t p = qsort_helper_partition(arr, min, max); QuickSort(arr, min, p - 1); QuickSort(arr, p + 1, max); } template <typename T> size_t Algorithms::qsort_helper_partition(T* arr, size_t min, size_t max) { T cmp = arr[min]; int i = min + 1, j = max; while(true) { while(cmp < arr[i]) ++i; while(arr[j] < cmp) --j; if(i >= j) break; swap(arr, i, j); } swap(arr, min, j); return j; } template <typename T> void Algorithms::swap(T* arr, size_t x, size_t y) { T tmp = arr[x]; arr[x] = arr[y]; arr[y] = tmp; }
用法Q(Z有标准库的排序法Q当然只是调一下,没有什么可说的了)
#include "Algorithms.h" #include <iostream> #include <vector> #include <algorithm> int _tmain(int argc, _TCHAR* argv[]) { int arr[] = {4, 8, 3, 7, 1, 5, 6, 2}; for(size_t i = 0; i != 8; ++i) { std::cout<<arr[i]<<" "; } std::cout<<std::endl; Algorithms::QuickSort(arr,0, 7); for(size_t i = 0; i != 8; ++i) { std::cout<<arr[i]<<" "; } std::cout<<std::endl; std::vector<int> vec; vec.push_back(3); vec.push_back(1); vec.push_back(4); vec.push_back(1); vec.push_back(7); vec.push_back(6); for(std::vector<int>::iterator iter = vec.begin(); iter != vec.end(); ++ iter) { std::cout<<*iter<<" "; } std::cout<<std::endl; std::sort(vec.begin(), vec.end()); for(std::vector<int>::iterator iter = vec.begin(); iter != vec.end(); ++ iter) { std::cout<<*iter<<" "; } std::cout<<std::endl; return 0; }