C++ std :: string替代strcpy?

Use*_*ser 2 c++ stdstring strcpy

我知道有一个类似的标题问题,但我想知道我对这个具体案例的选择.

MSVC编译器发出有关strcpy的警告:

1>c:\something\mycontrol.cpp(65): warning C4996: 'strcpy': This function or
variable may be unsafe. Consider using strcpy_s instead. To disable
deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
Run Code Online (Sandbox Code Playgroud)

这是我的代码:

void MyControl::SetFontFace(const char *faceName)
{
    LOGFONT lf;

    CFont *currentFont = GetFont();
    currentFont->GetLogFont(&lf);
    strcpy(lf.lfFaceName, faceName); <--- offending line
    font_.DeleteObject();
    // Create the font.
    font_.CreateFontIndirect(&lf);

    // Use the font to paint a control.
    SetFont(&font_);
}
Run Code Online (Sandbox Code Playgroud)

注意font_是一个实例变量.LOGFONT是一个windows结构,lfFaceName定义为TCHAR lfFaceName[LF_FACESIZE].

我想知道的是我可以做以下事情(如果不是为什么不这样做):

void MyControl::SetFontFace(const std::string& faceName)
...
  lf.lfFaceName = faceName.c_str();
...
Run Code Online (Sandbox Code Playgroud)

或者如果有完全不同的替代方案,请告诉我.

zwo*_*wol 8

您获得安全警告的原因是,您的faceName参数可能指向一个比字符长的字符串,LF_FACESIZE然后strcpy会盲目地覆盖结构lfFaceName中的任何内容LOGFONT.你有一个错误.

你应该不会改变盲目修复bug strcpy来strcpy_s,这是因为:

  1. 该*_s功能是不可移植微软的发明,几乎所有这些重复的其他C库函数的功能是便携.永远不要使用它们,即使是在一个不打算携带的程序中(如图所示).
  2. 盲目的改变往往不会真正解决这类错误.举例来说,"安全"变种strcpy(strncpy,strlcpy,strcpy_s)只是截断字符串,如果它太长,在这种情况下,将让你尝试加载错误的字体.更糟糕的是,strncpy 当它这样做时,省略了NUL终结器,所以CreateFontIndirect如果使用那个,你可能只是将崩溃移到里面.在正确的解决方法是前面检查的长度,如果它是太长无法全部操作.在这一点上strcpy变得安全(因为你知道它不会太长),虽然我更喜欢,memcpy因为它让我想到的代码的未来读者显而易见.
  3. TCHAR并且char是不一样的东西; 将C风格的const char *字符串或C++ 复制std::string到TCHAR没有正确编码转换的数组中可能会产生完全无意义的错误.(根据TCHAR我的经验,使用is总是一个错误,最大的问题是像这样的代码似乎在ASCII构建中正常工作,并且仍然会在UNICODE模式下编译,但在运行时会发生灾难性的失败.)

你当然可以std::string用来帮助解决这个问题,但它不会让你不需要检查长度并手动复制字符串.我可能会这样做.请注意,我正在使用LOGFONTW和CreateFontIndirectW来自UTF-8的显式转换std::string.另请注意,其中的大块是MSDN中的货物,并且没有一个已经过测试.抱歉.

void MyControl::SetFontFace(const std::string& faceName)
{
    LOGFONTW lf;
    this->font_.GetLogFontW(&lf);

    int count = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
                                    faceName.data(), faceName.length(),
                                    lf.lfFaceName, LF_FACESIZE - 1)
    if (count <= 0)
        throw GetLastError(); // FIXME: use a real exception

    lf.lfFaceName[count] = L'\0'; // MultiByteToWideChar does not NUL-terminate.

    this->font_.DeleteObject();
    if (!this->font_.CreateFontIndirectW(&lf))
        throw GetLastError(); // FIXME: use a real exception

    // ...
}
Run Code Online (Sandbox Code Playgroud)