预分配 std::string 以传递到 WinAPI

Preallocating std::string to pass into a WinAPI

我想知道下面的代码是否正确?我 运行 它在较旧版本的 VS 2008 上,用于 Windows-only C++ 项目。

我的目标是在 std::string 中预分配内存以将其传递到知道所需字符大小的 WinAPI 中:

//'hWnd' = window handle
int nLn = GetWindowTextLength(hWnd);

//Text variable to collect text in
std::wstring str;
str.reserve(nLn);

GetWindowText(hWnd, (LPTSTR)str.data(), nLn);

我担心的是 str.data() returns const wchar_t *GetWindowText() 请求 LPTSTR,这不是 const 缓冲区。类型转换可以吗?

不,根据 C++ 标准,您不能这样做。但是如果你使用 std::vector<wchar_t> 而不是 std::wstring:

就可以做到
int nLn = GetWindowTextLength(hWnd);
std::vector<TCHAR> str(nLn); // size, not reserve()
int size = GetWindowText(hWnd, str.data(), nLn);
str.resize(size); // will make empty in case of error

另请参阅:writing directly to std::string internal buffers

我不能代表 VS2008,但是大多数 pre-C++11 遵循 &str[0] + i == str.data() + i 的 C++11 规则,所以 &str[0] 可以工作并且不需要任何casts1,这比颠覆类型系统和标准要安全得多。请小心,因为不允许覆盖字符串后的空终止符,即使使用另一个空终止符也是如此。如果您不能为 VS2008 保证这一点,那么 std::vector<wchar_t> 将是首选解决方案。

但是,你还有另一个问题。 reserve 不会阻止越界访问 str 的未定义行为。边界基于 size(),而不是 capacity()。您需要将 reserve 更改为 resize

即便如此,你还有另一个问题。 GetWindowTextLength 不包括空终止符。您必须在该调用后的代码中使用 nLn + 1

随着所做的更改,这将适用于任何符合 C++11 的实现,包括一些 pre-C++11 实现,并忽略错误检查:

int nLnWithNul = GetWindowTextLength(hWnd);

std::wstring str(nLnWithNul, '[=10=]'); // bit of a shorthand for the resize call
int nCopiedLn = GetWindowText(hWnd, &str[0], nLnWithNul);
str.resize(nCopiedLn);

最后一个 resize 调用处理了比 nLnWithNul 短的复制标题。例如,如果标题在 GetWindowTextLength 被调用后缩小。调整大小后,字符串将仅包含复制的文本,不包括其后的任何 NUL 字符。


1:参见my previous question的答案。