在现代 C++ 应用程序中使用 Win32 代码时,是不是应该使用正确的转换?
Posted
技术标签:
【中文标题】在现代 C++ 应用程序中使用 Win32 代码时,是不是应该使用正确的转换?【英文标题】:When using Win32 code in your modern C++ app, should you use proper casting?在现代 C++ 应用程序中使用 Win32 代码时,是否应该使用正确的转换? 【发布时间】:2009-04-27 01:11:36 【问题描述】:例如,可以在整个 MSDN 文档中找到以下演员表:
(LPTSTR)&lpMsgBuf
我应该麻烦将其转换为:
static_cast<LPTSTR>(&lpMsgBuf);
或者我应该保留所有惯用的 C 风格 Win32 部分,因为它们通常在文档中找到,并为我的其余代码保存更惯用的 C++ 样式/用法?
【问题讨论】:
【参考方案1】:引入新样式转换是有原因的:它们更安全、解释性/自我注释性更强、更易于查看且更易于使用 grep 查找。
所以使用它们。
通过更详细的解释,我们的意思是你不能只是投射到某个东西,你必须说为什么你正在投射(我在继承层次结构中投射 (dynamic_cast),我的演员是实现定义并且可能不可移植(reinterpret_cast),我正在抛弃常量(const_cast)等)。
它们故意长而丑陋,以便演员在读者面前跳出来(并阻止使用过多演员的编程风格)。
它们更安全,因为例如,如果不明确这样做,您就无法抛弃 constness。
【讨论】:
【参考方案2】:我不会在我的代码中添加旧式强制转换或新式强制转换,而是利用 C++ 的运算符重载来添加采用正确类型参数的 Windows API 函数的内联重载版本。 (只要为新开发人员记录了这一点,希望它不会太混乱。)
例如,FormatMessage
的第五个参数通常是LPTSTR
,但如果您传递FORMAT_MESSAGE_ALLOCATE_BUFFER
标志,则第五个参数是指向 LPTSTR 的指针。所以我会定义一个这样的函数:
inline DWORD WINAPI FormatMessage(
__in DWORD dwFlags,
__in_opt LPCVOID lpSource,
__in DWORD dwMessageId,
__in DWORD dwLanguageId,
__out LPTSTR *lpBuffer,
__in DWORD nSize,
__in_opt va_list *Arguments
)
assert(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER);
return FormatMessage(dwFlags, lpSource, dwMessageId, dwLanguageId, static_cast<LPTSTR>(&lpBuffer), nSize, Arguments);
【讨论】:
【参考方案3】:MSDN 文档与 C++ 标准。
我会选择后者。我认为这也是有效 C++ 中讨论的规则之一。您应该不在一个程序中混合使用两种样式。
【讨论】:
【参考方案4】:您根本不需要演员表——从外观上看,lpMsgBuf
是一个指向字符数组的指针(从上下文中不清楚它们是 ANSI 字符还是宽字符),您可以将其传递直接连接到各种 Win32 函数。
没有必要获取地址——如果lpMsgBuf
是一个静态数组(例如char lpMsgBuf[SIZE]
),那么获取它的地址是多余的,相当于它的第一个元素的地址。如果它是一个指针,那么获取它的地址会产生一个char**
(如果它很宽,则为一个wchar_t**
),这不是你想要传递的——如果一个Win32函数期待一个LPSTR
(即一个@987654327 @) 然后你将char**
转换为char*
,会导致很多错误。编译器不会让您将 char**
传递给期望 char*
而不进行强制转换的函数 - 使用强制转换使编译器静音是错误的,因为编译器试图告诉您一些重要的事情。
如果您在这种情况下无法在不强制转换的情况下传递您想要的参数,那么您几乎肯定会传递错误。修复代码,这样您就不需要演员表了。
【讨论】:
你我都来不及了。在我们打字的时候,一个答案已经被接受,说这个很可能充满错误的代码应该仍然很可能充满错误。真是浪费了我们的努力。 不幸的是,Windows API 有时确实需要像这样看似糟糕的强制转换。 FormatMessage 就是一个例子。 @Windows 程序员:真的,sigh @Josh Kelley:真的,尤其是关于某些 Windows 消息。 WPARAM 和 LPARAM 可以是任何东西。但是,如果没有 OP 试图调用的特定函数的上下文,我会假设最坏的情况。 是的,我只是举了一个例子,问题更多的是关于风格,而不是具体的例子,所以我认为我正确地标记了正确的答案,但我赞成这个,因为它也很有用.谢谢。【参考方案5】:如果 lpMsgBuf 已经是一个指向 T-string 的指针,并且如果 API 期望该指针直接指向一个 T-string,那么您根本不应该使用任何类型转换。换句话说,如果数据类型已经正确,那么最好不要使用不必要的强制转换。原因是过度使用强制类型转换会使您在强制类型转换关闭编译器的同时对错误视而不见。
如果 lpMsgBuf 已经是一个指向 T-string 的指针,并且如果 API 需要一个指向 T-string 的指针,但 API 希望指向指针的指针转换为指向 T-string 的类型指针(以及 API使用时会回退),那么你就做对了。
如果 lpMsgBuf 是指向其他东西(类型正确)的指针,并且 API 需要指向您的类型的指针,但 API 需要这些铸造游戏,那么您做对了。
如果 lpMsgBuf 是一个指向 T-string 的指针,并且如果 API 需要一个指向 T-string 的指针,那么通过使用 & 运算符,您正在构造一个指向 T-string 指针的指针,而 API 没有期望,并且通过使用强制转换,您告诉编译器将该指向指针的指针转换为指向 T-string 的类型指针,即使该值仍指向指针。因此,您成功关闭了编译器,但仍将垃圾结果传递给您或您的客户。
因此,如果没有更多关于哪个 MSDN 页面告诉您创建指向指针并为哪个 API 玩铸造游戏的详细信息,我们无法猜测您是否做得对,或者您是否误读了 MSDN,或者是否MSDN 告诉你写不好的代码。
现在,如果转换确实正确,那么选择使用哪种语法取决于您在程序其余部分中的风格。
【讨论】:
【参考方案6】:在大多数情况下,MSDN Win32 示例代码与 C 和 C++ 兼容。使用普通的旧 C 语言编写 Windows 程序是完全允许的(并且在某个时间点是标准做法)。
由于 C++ 转换在 C 中不可用,因此修改示例以利用它们需要维护两个示例代码副本。由于 C 代码与 C++ 中的代码一样工作,因此保留旧式类型转换会更容易。
【讨论】:
以上是关于在现代 C++ 应用程序中使用 Win32 代码时,是不是应该使用正确的转换?的主要内容,如果未能解决你的问题,请参考以下文章
使用 CreateProcess 从 Win32 C++ 应用程序启动 Java 应用程序时出错
在 C++(win32 应用程序)中使用 C# 类时出现 EEFileLoadException