在我测试过的所有 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 );
}
数据缓冲区似乎也是类中的第一个数据元素,通过调试器下的执行跟踪表明未调用转换运算符,代码纯粹基于类布局工作。
虽然可以很容易地重写原始代码来避免上述情况,但是否有必要并且可能会被 future 的 MFC 更新破坏?所涉及的代码库部分主要是第 3 方,我宁愿避免在没有充分理由的情况下更改它。
最佳答案
不,您编写的代码已损坏。正式地说,这是未定义的行为。您不应将对象传递给可变参数函数。这些类型的函数没有编译时类型检查(即,不是类型安全的),因此编译器不知道它应该调用隐式转换运算符来转换 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
唯一看到的就是指针,因此它会被解析当它看到 %s
说明符时正确。但我不建议依赖这种行为。它在形式上仍然是未定义的行为,即使它以特定于实现的方式工作。
诚然,此时 CString 的接口(interface)不太可能发生变化,更不可能因为它会破坏如此多的现有代码。但你永远不知道,当你正确编写代码时,它会让你的意图更加清晰。
关于c++ - MFC CString 中的可变参数和转换运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38850913/