将对象传递给var arg函数

passing an object to var arg function

本文关键字:var arg 函数 对象      更新时间:2023-10-16
变量argument函数如何将对象带到struct/class?例如:
CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %s"),a_csAge);

这里CString::Format是一个printf风格的变量argument函数,它接受这个CString对象。这怎么可能?

通过MFC代码进行了一些研究和调试后,发现了以下内容,希望它能帮助那些面临著名的静态代码分析器错误"将结构体'CStringT'传递给省略号"的人,这实际上也是我怀疑的来源。

http://www.gimpel.com/html/bugs/bug437.htm

Format函数是一个可变函数,取决于第一个参数中的格式说明符。第一个参数始终是char*。

它解析格式说明符(%s、%d、%i…(,并根据找到的格式说明符的索引读取var_arg数组,如果指定了%s,则直接转换为char*或int。

因此,如果指定了一个CString,并且相应的格式说明符是%s,则会在CString对象上直接转换char*。

CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %s"),a_csAge);
wcout<<a_csName;

将打印我的年龄是20

CString a_csName;
CString a_csAge(_T("20"));
a_csName.Format(_T("My Age is : %d"),a_csAge);
wcout<<a_csName;

将打印我的年龄是052134

所以这背后并没有什么智慧。只是一个直接演员。因此,我们传递POD或用户定义数据结构,它没有什么区别。

来源:http://msdn.microsoft.com/en-us/library/aa300688%28v=vs.60%29.aspx

  • 您可以自由地用CString对象替换const char*和LPCTSTR函数参数

可能CString是一个没有vtable的类,并且只有一个类型为char*的属性。这意味着sizeof(CString) == sizeof(const char*),并且如果将CString重新解释为const char*,则会得到一个工作的const char*

编译器不应接受将结构传递给函数的可变部分。但如果我没有错的话,在GCC中,当你将一个结构作为变量参数传递给时,它的内存会被复制(即没有使用复制构造函数(,并发出警告。我猜MSVC在那里也在做同样的事情,然后,Format方法只是假设给定的数据是const char*

让我给你举一个替代的例子。假设你有一个没有vtable的类,它只有成员是int。如果你把它传递给一个可变参数,编译器会复制这个对象的内容,这只是一个int。在函数实现中,你(不知何故(知道你收到了一个int。然后查询参数,要求int。由于在内存级别上,您的类与int没有什么不同,所以一切都会"正常"工作。该函数将访问类的int属性。

我最近不得不清除这个Lint错误,并偶然发现了这个线程。

尽管对正在发生的强制转换进行了有趣的调查,但避免Lint警告的最简单方法是使用CString方法Get.String()传递CString指针,而不是CString结构,如以下中所示

a_csName.Format(_T("My Age is : %d"), a_csAge.GetString());

让我开始以不同的方式回答这个问题。

struct Value
{
   int nValue;
   float fOtherValue;
};
int main()
{
    Value theValue;
    theValue.nValue = 220;
    theValue.fOtherValue = 3.14f;
    printf("Value: %d", theValue);
}

此代码将打印220而不会出现任何问题。但如果你在theValue之后通过第二个参数,它不会:

printf("Values: %d, %f", theValue, theValue.fOtherValue);

由于第一个变量参数theValue不满足%d参数指定的大小。因此,3.14不会(可能不会(被显示。我们不讨论printf、栈上的参数推送、va_start和类似的东西是如何工作的。

类似地,当我们以这种方式设计String类时:

struct String
{
    char* pData;
public:
    String()
    {
        pData = new char[40];
        strcpy_s(pData, 40, "Sample");
    }
};

并以这种方式使用:

String str;
printf("%s", str);

这肯定行得通。

但是,参数和调用堆栈损坏怎么办?如果类的大小(如Value(大于格式(%d(指定的数据大小(对于Value为4(,该怎么办。在这种情况下,设计的类需要确保给定类的大小与printf函数使用的参数大小相同。

我不会提及有关它的详细信息,但CString使用内部类CStringData。后一个类保存字符串、长度、引用计数等。CString(实际上是CSimpleStringT(使用这个类,但只保存由CStringData管理的char/wchar_t的指针。

在很大程度上,CString被实现为所提到的String类(就数据和sizeof而言(。