在我测试过的所有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更新破坏?涉及的代码库部分主要是第三方,我宁愿在没有充分理由的情况下改变它。
答案 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}}说明符。但我不建议依赖这种行为。它仍然是正式未定义的行为,即使它以特定于实现的方式工作。
当然,CString的接口在这一点上不太可能发生变化,甚至不太可能因为它会破坏现有的代码。但是你永远不会知道,当你正确编写代码时,它会使你的意图更清晰。