【问题标题】:Why must CString be cast to LPCSTR when using variadic functions?为什么在使用可变参数函数时必须将 CString 强制转换为 LPCSTR?
【发布时间】:2013-01-17 09:28:44
【问题描述】:

根据this site,有谁知道为什么这个代码不正确:

CString First( "John" );
CString Last( "Doe" );

CString Name;

Name.Format( "%s %s", First, Last ); // Name may be garbage text

Name.Format( "%s %s", (LPCSTR)First, (LPCSTR)Last ); // this is the correct way

CString::Format 的微软文档说:

...当您将字符串作为可选参数传递时,您必须将其显式转换为 LPCTSTR...

我总是使用“错误”的方式(没有 LPCSTR 演员),我从来没有遇到过问题。

我在这里错过了什么吗?

【问题讨论】:

标签: c++ mfc


【解决方案1】:

因为在 Format 函数内部没有进行强制转换,就像在为 printf() 指定参数时没有进行强制转换一样

如果您编写 printf( "%d", a );,则不会对 a 进行强制转换,因为 printf 假定 %d 已经告诉 a 是什么数据类型。

为了确保CString 转换为LPCSTR 即%s,您需要转换参数,该参数将调用CString 上的运算符,返回LPCSTR。在 CString 的更高版本中,字符串被存储,因此当您在没有强制转换的情况下编写时,它仍然会打印为 LPCSTR,但要确保最好进行强制转换。

或者换一种说法:当可变参数函数通过参数时,它使用格式说明符来知道参数的大小,如果格式说明符与参数不匹配,你会得到垃圾。由于 CString 没有格式说明符,因此您需要将 CString 转换为 LPCSTR。

顺便说一句,您应该在 C++ 中使用 static_cast<LPCSTR>(First), static_cast<LPCSTR>(Last)

【讨论】:

    【解决方案2】:

    转换为 LPCSTR 调用转换运算符 operator LPCSTR()(应该是 operator const char *()。

    不调用 LPCSTR 意味着您正在传递整个 CString 对象,然后盲目地使用底层结构的前 32 位或 64 位作为 char 指针。

    回顾一下:LPCSTR(str) 正在调用一个方法来确保正确的行为,而 str 是在瞎玩弄。

    【讨论】:

    • 实际上就是这样。如果将 CString 对象作为可变参数,编译器实际上会将 CString 对象的前 32 位压入堆栈。这适用于 CString,因为 CString 类的设计方式是任何 CString 对象的 32 位都是指向实际字符串的指针。
    • @MichaelWalz:你的意思是你对你的代码是未定义的行为感到满意,这取决于CString 的实现细节可能会或不会改变,因为在你的特定情况下它似乎工作.现在您的问题是为什么要强制转换? 答案很简单,以避免未定义的行为。
    • @DavidRodríguez-dribeas,我没有说我很高兴,我只是说实际发生的事情。但是,如果 CString 类的布局发生变化,使用强制转换肯定会节省您的时间。
    • @MichaelWalz 您是否有可变参数的来源,仅将前 32/64 位推入调用堆栈?
    • @MichaelWalz 没什么大不了的,但我认为我已经将结构直接传递到可变参数中,如果所有参数在调用时都适合 32/64 位,则之前这是不可能的。
    【解决方案3】:

    我认为(对不起,我现在没有可用的 MFC 来检查)只是出于效率原因需要强制转换,以避免在参数类型未知时 MFC 必须执行的无用临时复制(CString 被延迟复制修改)。

    诀窍在于 CString 内存分配为缓冲区 (TCHAR*) 以外的一些基本数据保留空间之前 this,例如引用计数器。

    然后,当您将 CString 按值传递给可变参数函数时,它会“看到”缓冲区(指针) 强制转换“提取”只是缓冲区,而不调用复制构造函数。

    【讨论】:

    • 实际上,当这样的对象作为可变参数传递时,并没有创建临时的 CString 对象。请参阅我在上一个答案中的评论。
    • @MichaelWalz:我在重新考虑:应该调用复制构造函数(按值传递参数),但由于“修改时复制”,没有缓冲区复制。
    • 仅供参考:当您不使用强制转换时,不会调用复制构造函数。使用强制转换时,会调用强制转换运算符,但不会调用复制构造函数。请参阅上面 Jeffery Thomas 回答的 cmets。
    【解决方案4】:

    因为如果您使用子类 CString 参数,它会中断。

    【讨论】:

    • 使用 Microsoft 编译器,即使您将 CString 子类化,它也会继续工作,但如果 CString 包含虚函数,那么您会得到垃圾,因为在这种情况下,前 4 个字节不再是指向字符串的指针,而是指向虚函数表的指针。
    • 这解释得很清楚!不幸的是,我所有衍生的 CString 类都使用虚拟析构函数进行清理。我不确定我能否摆脱非虚拟析构函数。我不使用 CString* ptr = new CMyStringEx(),但其他程序员可能有。
    猜你喜欢
    • 2014-01-26
    • 2020-06-04
    • 2011-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    相关资源
    最近更新 更多