MFC CString

时间:2016-08-09 12:34:12

标签: c++ mfc variadic-functions

在我测试过的所有Visual C ++版本的MFC中,以下代码编译并运行;

CString A = "String A", B;
B.Format("The value of A is %s", A);

但会产生警告

C6284, Object passed as _Param_(2) when a string is required in call to 
'ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > >::Format'
 Actual type: 'class ATL::CStringT<char,class StrTraitMFC<char,class ATL::ChTraitsCRT<char> > >'.

查看CString的源代码及其CStringT,CSimpleStrT的祖先,同时有一个转换运算符可以有效地转换为char *,如下所示;

operator PCXSTR() const throw()
{
    return( m_pszData );
}

似乎数据缓冲区是类中的第一个数据元素,并且在调试器下跟踪执行建议不会调用转换运算符,并且代码纯粹基于类布局工作。

虽然可以轻松地重写原始代码以避免上述情况,但它是否有必要并且可能会被未来的MFC更新破坏?涉及的代码库部分主要是第三方,我宁愿在没有充分理由的情况下改变它。

1 个答案:

答案 0 :(得分:8)

没有你所写的代码被破坏了。从官方上讲,这是未定义的行为。您不应将对象传递给可变参数函数。这些类型的函数没有编译时类型检查(,不是类型安全的),因此编译器不知道它应该调用用于将CString对象转换为PCXSTR的隐式转换运算符。您必须显式执行转换,使用强制转换或调用返回指向底层C样式字符串缓冲区的指针的成员函数。

对于所有可变参数函数都是如此。甚至像printf那样简单。以下代码错误:

std::string str = "world";
printf("Hello, %s", str);   // <-- this code is WRONG!

你必须这样写:

std::string str = "world";
printf("Hello, %s", str.c_str());

对于MFC *

也是如此
CString str = TEXT("world");
CString buffer;
buffer.Format(TEXT("Hello, %s"), static_cast<LPCTSTR>(str));
// alternatively:
// buffer.Format(TEXT("Hello, %s"), str.GetString());

这是不在C ++中使用可变参数函数的好理由。喜欢流类型,它们是类型安全的并且做对了。

您收到的警告正在尝试提醒您此问题。尽管可变函数不是类型安全的,但这是一个常见的问题,编译器供应商已经付出了很多努力来尝试解析格式字符串,寻找插入,并将这些与传递的参数相匹配。由于我所描述的原因,它在这种情况下发现不匹配,并且发出警告C6284

* 实际上,在这种情况下,碰巧无论如何为CString工作。这是因为,正如您在调试器中发现的那样,该类是专门设计的,因此它的第一个成员是指向C风格字符串缓冲区的指针。因此,当它以非类型安全的方式将值传递给类似printf的可变函数时,printf看到的唯一内容就是指针,因此当它看到{时,它会被正确解析{1}}说明符。但我不建议依赖这种行为。它仍然是正式未定义的行为,即使它以特定于实现的方式工作。

Microsoft's documentation specifically tells you to pass CString objects to variadic functions by performing an explicit cast to a char-pointer

当然,CString的接口在这一点上不太可能发生变化,甚至不太可能因为它会破坏现有的代码。但是你永远不会知道,当你正确编写代码时,它会使你的意图更清晰。