将对象传递给var arg函数

时间:2013-02-05 14:00:16

标签: c++ mfc cstring

变量arguement函数如何将对象带入struct / class? 例如:

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

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

4 个答案:

答案 0 :(得分:4)

来自: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中,当你将一个struct传递给variadic参数时,它的内存被复制(即没有使用复制构造函数)并发出警告。我想MSVC在那里也是这样做的,然后,Format方法只是假设给定的数据是const char*

让我再举一个例子。假设你有一个没有vtable的类,只有成员才是int。如果将它传递给可变参数,编译器将复制此对象内容,这只是一个int。在函数实现上,您(不知何故)知道您收到了int。然后查询要求int的参数。因为,在内存级别,你的课程与int没什么不同,所以事情会很好“。该函数将访问该类的int属性。

答案 1 :(得分:4)

经过MFC代码的一些研究和调试后发现,希望它能帮助那些面临着名的静态代码分析器错误“将结构'CStringT'传递给省略号”,这实际上也是我怀疑的来源。 / p>

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

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

它解析格式说明符(%s,%d,%i ...)并根据找到的格式说明符的索引读取var_arg数组,如果%s则直接转换为char *或者如果%d则转换为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;

打印My Age是20

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

打印My Age是052134

所以这背后没有任何情报。只是直接演员。因此,我们传递POD或用户定义的数据结构,它没有什么区别。

答案 2 :(得分:2)

我最近不得不清除这个Lint错误并偶然发现了这个帖子。

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

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

答案 3 :(得分:0)

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

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)大于格式(Value)指定的数据大小(%d为4),该怎么办?那么,在这种情况下,设计的类需要确保给定类的大小与printf函数使用的参数大小相同。

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

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