根据C ++标准我发现here:
可以转移到块中,但不能转移到块中 用初始化绕过声明。
我工作场所的 C风格编码(我的意思是除文件扩展名之外,一切实际上都是C - 没有例外,没有模板,没有实际的类)遵守指定的here规则,即仅从一个地方退出一个功能,仅在达到完全成功流程时执行所有权转移,对我们尚未转让的所有权进行清理等。 这是一个小例子(为简洁起见,省略了错误代码枚举和其他事项):
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下编译它时,编译器惋惜goto
语句跨越{pszFilePath
初始化的问题1}}。
从第一个链接我可以看到,如果我将初始化分离为声明和赋值,这将很好地形成,因为PWSTR
是POD类型。
我想在MinGW-W64 下编译我的项目,而不对我的代码库进行大量修改。有没有办法让我这样做(编译器标志,希望如此)?
我很清楚使用RAII类在C ++中编写代码的惯用方法,但代码库是扁平的C风格线性代码。我也很清楚开发人员强烈反感goto
,以及任何带有goto
的代码都可以在没有代码的情况下编写。我不是要求风格指导,而是以最省力的方式来解决这个非常具体的问题。
答案 0 :(得分:3)
解决这类事情的最不痛苦的方法可能是引入大括号来限制问题变量的范围,例如:改变这个块:
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;
}
答案 1 :(得分:1)
这个函数中的所有getos都可以避免:
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;
}
答案 2 :(得分:1)
为了避免使用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”方法将无效,因为文件句柄和内存将不会被清除。上述方法可确保清理工作。
答案 3 :(得分:1)
在这个特定的例子中,通过声明pszFilePath
static:
static const PTSTR pszFilePath = _T("C:\\temp\\bla.txt");
这可能不是一般解决方案,但在每种情况下,最小变更解决方案在任何情况下都可能有所不同,因此您可能需要选择适当的方法来应用。