C++使用std::string、std::wstring作为缓冲区

C++ using std::string, std::wstring as a buffer

本文关键字:std 缓冲区 wstring string 使用 C++      更新时间:2023-10-16

使用WinAPI时,您经常会遇到一些将LPWSTR或LPSTR作为参数的方法。有时这个指针实际上应该是指向缓冲区的指针,例如:

  int GetWindowTextW(HWND hWnd, LPWSTR lpString, int nMaxCount);

对这样的缓冲区使用std::wstring是个好主意吗?特别是在我强烈需要生成std::wstring作为结果而不能用vector<wchar_t>代替它的情况下?

std::wstring myWrapper(HWND hWnd){
    auto desiredBufferSize = GetWindowTextLengthW(hWnd);
    std::wstring resultWstr;
    resultWstr.resize(desiredBufferSize);
    auto ret = GetWindowText(hWnd,
                             const_cast<wchar_t*>(resultWstr.data()), // const_cast
                             resultWstr.size());
    // handle return code code
    return resultWstr;
}

data()c_str()字符串方法都返回const指针,所以我们必须使用const_cast来去除const,这有时是一个坏兆头。在这种情况下,这是一个好主意吗?我能做得更好吗?

使用字符串作为C字符串

const char*std::string的自动类型转换,但不能反过来。

字符"\0"对于std::string来说并不特殊。

  • 用于写访问的&s[0]

确保字符串大小(不仅仅是容量)足够大,可以进行C风格的编写。

  • 只读访问的s.c_str()

仅在下一次调用非常量方法之前有效。

代码示例:

const int MAX_BUFFER_SIZE = 30;         // Including NULL terminator.         
string s(MAX_BUFFER_SIZE, '');      // Allocate enough space, NULL terminated
strcpy(&s[0], "This is source string.");    // Write, C++11 only (VS2010 OK)
printf("C str: '%s'n", s.c_str());     // Read only: Use const whenever possible.

选择漂亮的标准wstring很诱人。然而,扔掉const永远都不好。。。

这里有一个临时字符串包装器,它自动创建一个缓冲区,将指针传递给winapi函数,并将缓冲区的内容复制到您的字符串中,然后干净地消失:

auto ret = GetWindowText(hWnd,
                         tmpstr (resultWstr, desiredBufferSize), 
                         resultWstr.size());

此解决方案适用于任何在字符指针返回之前写入字符指针的windows API函数(即无assync)。

它是如何工作的

它基于C++标准§12.2第3点:"临时对象被销毁是评估完整表达式的最后一步,完整表达式(从词汇上)包含了创建它们的点。(…)销毁临时对象的值计算和副作用只与完整表达式相关,而与任何特定的子表达式无关。"。

这里是实现:

typedef std::basic_string<TCHAR> tstring;  // based on microsoft's TCHAR
class tmpstr {
private:
    tstring &t;      // for later cpy of the result
    TCHAR *buff;     // temp buffer 
public:
    tmpstr(tstring& v, int ml) : t(v) {     // ctor 
          buff = new TCHAR[ml]{};           // you could also initialize it if needed
           std::cout << "tmp createdn";    // just for tracing, for proof of concept
        }
    tmpstr(tmpstr&c) = delete;              // No copy allowed
    tmpstr& operator= (tmpstr&c) = delete;  // No assignment allowed
    ~tmpstr() {                              
          t = tstring(buff);                // copy to string passed by ref at construction
          delete buff;                      // clean everyhing
          std::cout<< "tmp destroyed";      // just for proof of concept.  remove this line
        }
    operator LPTSTR () {return buff; }  // auto conversion to serve as windows function parameter without having to care
}; 

正如您所看到的,第一行使用typedef,以便与几个窗口编译选项兼容(例如,Unicode或非Unicode)。当然,如果您愿意,您可以将tstringTCHAR替换为wstringwchar_t

唯一的缺点是必须将缓冲区大小作为参数tmpstr构造函数和windows函数的参数来重复。但这就是为什么你要为函数写一个wrapper,不是吗?

对于字符串缓冲区,为什么不只使用char数组?:)

DWORD username_len = UNLEN + 1;
vector<TCHAR> username(username_len);
GetUserName(&username[0], &username_len);

公认的解决方案是过度思考的好例子。