C转到初始化,MinGW-W64 vs MSVC ++

我在这里发现,根据C ++标准:

可以将其转换为块,但不能以绕过具有初始化的声明的方式。

在我的工作环境中的C编码风格 (我的意思是,除了文件扩展名以外, 一切实际上都是C – 没有例外,没有模板,也没有实际的类)遵循这里指定的规则,即仅从一个函数退出只有在达到完全成功的stream程时执行所有权转移,对我们没有转移所有权的本地人进行清理,等等。下面是一个小例子(错误代码枚举以及为简洁起见而省略的其他内容):

int func() { int iStatus = -1; PVOID pvSomeData = NULL; HANDLE hFile = INVALID_HANDLE_VALUE; pvSomeData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, BUFFER_SIZE); if (nullptr == pvSomeData) { iStatus = 1; goto lblCleanup; } const PTSTR pszFilePath = _T("C:\\temp\\bla.txt"); _tprintf(_T("Writing some stuff into '%s'"), pszFilePath); hFile = CreateFile(pszFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { iStatus = 2; goto lblCleanup; } // do more stuff, fill pvSomeData, write it to the file, transfer ownership, etc. // success iStatus = 0; lblCleanup: if (INVALID_HANDLE_VALUE != hFile) { CloseHandle(hFile); } if (nullptr != pvSomeData) { HeapFree(GetProcessHeap(), 0, pvSomeData); } return iStatus; } 

这种C风格的代码在MSVC ++下编译得很好,但是当试图在MinGW-W64下编译它时,编译器会pszFilePath goto语句跨越pszFilePath初始化的pszFilePath 。 从第一个链接我可以看到,如果我将初始化分成一个声明和一个赋值,那么这将是很好的forms,因为PWSTR是一个PODtypes。

我想在MinGW-W64 编译我的项目, 而不对代码库进行大量的修改 。 有没有办法让我这样做(编译器标志,希望)?

我非常清楚在C ++中用RAII类编写代码的习惯用法,但代码库是扁平的C型线性代码 。 我也很清楚开发人员强烈的厌恶情绪,以及任何带有goto的代码片段都可以被编写出来。 我不要求风格指导,而是要以最less的努力解决这个非常具体的问题。

解决这个问题的最不痛苦的方法可能是引入大括号来限制问题变量的范围,例如改变这个块:

 const PTSTR pszFilePath = _T("C:\\temp\\bla.txt"); _tprintf(_T("Writing some stuff into '%s'"), pszFilePath); hFile = CreateFile(pszFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { iStatus = 2; goto lblCleanup; } // do more stuff, fill pvSomeData, write it to the file, transfer ownership, etc. // success iStatus = 0; 

至:

 { const PTSTR pszFilePath = _T("C:\\temp\\bla.txt"); _tprintf(_T("Writing some stuff into '%s'"), pszFilePath); hFile = CreateFile(pszFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { iStatus = 2; goto lblCleanup; } // do more stuff, fill pvSomeData, write it to the file, transfer ownership, etc. // success iStatus = 0; } 

所有在这个函数中的gotos都是可以避免的:

 int func() { int iStatus = -1; PVOID pvSomeData = NULL; HANDLE hFile = INVALID_HANDLE_VALUE; if (nullptr == pvSomeData) { iStatus = 1; } else { const PTSTR pszFilePath = _T("C:\\temp\\bla.txt"); _tprintf(_T("Writing some stuff into '%s'"), pszFilePath); hFile = CreateFile(pszFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { iStatus = 2; } else { // do more stuff, fill pvSomeData, write it to the file, transfer ownership, etc. // success iStatus = 0; } } if (INVALID_HANDLE_VALUE != hFile) { CloseHandle(hFile); } if (nullptr != pvSomeData) { HeapFree(GetProcessHeap(), 0, pvSomeData); } return iStatus; } 

为了避免goto,以及能够维护代码而不引入错误,可以使用以下方法:

 // a structure that handles the clean up struct RAIIHandler { PVOID* ptrData; HANDLE *fileHandle; RAIIHandler(PVOID* p, HANDLE* fh) : ptrData(p), fileHandle(fh) {} ~RAIIHandler() { if (INVALID_HANDLE_VALUE != *fileHandle) CloseHandle(*fileHandle); if (nullptr != *pvSomeData) HeapFree(GetProcessHeap(), 0, *pvSomeData); } }; int func() { int iStatus = -1; PVOID pvSomeData = NULL; HANDLE hFile = INVALID_HANDLE_VALUE; // create a handler that gets cleaned up on return RAIIHandler handler(&pvSomeData, &hFile); pvSomeData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, BUFFER_SIZE); if (nullptr == pvSomeData) return 1; const PTSTR pszFilePath = _T("C:\\temp\\bla.txt"); _tprintf(_T("Writing some stuff into '%s'"), pszFilePath); hFile = CreateFile(pszFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) return 2; // do more stuff, fill pvSomeData, write it to the file, transfer ownership, etc. return iStatus; } 

诀窍是我们创建一个对象,指向将要清理的项目。 当函数因任何原因返回时,对象将调用它的析构函数,必要时清理句柄和内存。 这被称为RAII (资源获取是初始化),并在SO上的许多线程中讨论。

你的代码的问题是,你不准备处理意外,如果和/或当它发生,这是可怕的exception 。 如果你现在有代码,或者将来可能会throw代码,那么你的“goto”方法将不起作用,因为文件句柄和内存不会被清理。 上述方法确保清理发生。

在这个特定的例子中,通过声明pszFilePath static可能最容易解决这个问题:

 static const PTSTR pszFilePath = _T("C:\\temp\\bla.txt"); 

这也许不是一个通用的解决方案,但是在任何情况下,最小的变更解决方案在任何情况下都可能有所不同,因此您需要一些适当的方法来适当地应用。