在 CString::Format 中使用 CString 对象

Bar*_*ani 3 c++ mfc

我可以用CStringCString::Format如下:

CString text = _T("text");
CString format;
format.Format(_T("%s"), text);
Run Code Online (Sandbox Code Playgroud)

在 MFC 源文件和 MFC 书籍中可以看到相同的方法。例如:

//From MFC file:
//C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\src\mfc\
//    afxeditbrowsectrl.cpp
BOOL CMFCEditBrowseCtrl::OnIllegalFileName(CString& strFileName)
{
    CString strError;
    CString strMessage;
    ...
    strMessage.Format(_T("%Ts\r\n%Ts"), strFileName, strError);
    //I don't know what is the `T` in `"%Ts"` !
    ...
}
Run Code Online (Sandbox Code Playgroud)

然而,其他 MFC 源文件使用LPCTSTR强制转换。例子:

format.Format(_T("%s"), (LPCTSTR)text);
Run Code Online (Sandbox Code Playgroud)

演员表是强制性的吗?

IIn*_*ble 7

CStringT类模板的文档中:

您可以自由地将CStringT对象替换为PCXSTR函数参数。

每当函数参数需要常量 C 样式字符串时,您就可以传递一个CStringT对象,该对象通过调用运算符 PCXSTR() 进行隐式转换。

另一方面,具有可变参数的函数采用无类型参数列表。在这种情况下,传递一个(语义上)预期的CStringT对象PCXSTR是不安全的。编译器无法知道它应该发出代码来执行隐式转换。您负责自己执行转换,方法是调用operator PCXSTR()或调用GetString()类成员。

就书面合同而言,这是如此。要理解为什么CStringT通过可变参数列表传递 a 仍然(技术上)安全,有助于查看实现1

CSimpleStringT类模板对于所有支持的字符串类的基础。它包含一个 type 的(私有)数据成员PCXSTR,没有虚函数,使其成为标准布局类型。因此,二进制表示与其唯一成员的表示相同,即指向以零结尾的字符数组的指针。


与此问答的讨论无关,但为了完整起见,这里简要说明如何CSimpleStringT跟踪附加数据(如字符串长度或引用计数)。唯一的数据成员m_pszData指向CStringData类型的结构,该结构由一个大小恒定的初始序列组成,存储簿记信息,紧跟其后的是一个长度为 的字符数组nAllocLength + 1m_pszData指向这个数组,额外的数据通过指针算法获取,由私有GetData()成员实现:

CStringData* GetData() const throw()
{
    return( reinterpret_cast< CStringData* >( m_pszData )-1 );
}
Run Code Online (Sandbox Code Playgroud)

类布局总结如下图:

CStringData* GetData() const throw()
{
    return( reinterpret_cast< CStringData* >( m_pszData )-1 );
}
Run Code Online (Sandbox Code Playgroud)

1 这是基于 Visual Studio 2017 随附的实现,理论上可以更改,恕不另行通知。