变量参数列表如何与C ++中的重载对象一起使用?

时间:2015-11-11 23:05:22

标签: c++ string mfc operator-overloading

将变量参数传递给语句时,类型信息不会被传递。

想象一下这个例子使用一个字符串对象(我使用MFC / ATL' CString)

CString name = "kevin";
printf("Hi %s", name);

printf语句(或CString)如何知道获取/返回LPCSTR运算符,从而获得指向该字符串的指针,而不是指向该对象的指针?

我试图编写自己的字符串类(它需要在一个巨大的项目中替换CString),但是当我执行上面的printf时,它会因为没有得到正确的值而出现乱码为了名字。

3 个答案:

答案 0 :(得分:2)

真正的答案与对运算符LPCTSTR()的隐式调用无关,我认为它的实际工作方式有点聪明。

要实现字符串类,当然需要字符串的数据缓冲区以及一些其他数据字段,例如字符串的长度,可能分配了多少内存,可能是引用计数,等

但是,在传递CString参数时,如何使可变参数函数看到C风格的字符串?您可以将数据缓冲区字段(指向char *的指针)作为第一个成员,但是所有其他字段(如字符串长度)也会意外地被压入堆栈,就好像它们是额外的函数参数一样。

因此,接下来要尝试的是将所有字段打包到一个内存结构中,该结构与字符串< d字符数据缓冲区一起分配。如果此数据缓冲区位于聚合结构的开头,则期望C字符串的可变参数函数将看到此结构的开头。如果在数据缓冲区的末尾放置一个终端null,那么在其他数据字段之前,printf将在它击中您不希望它看到的数据之前停止读取字符串数据,并且一切正常!

但是后来你意识到你有一个新问题 - 字符数据缓冲区的长度是可变的,并且"长度"字段现在位于字符数据后的内存中,因此您需要知道" length"为了知道"长度"可以找到字段。

所以你可以移动"长度"和所有其他额外字段到结构的开始,在字符数据之前的内存中?当然不是,printf现在会在真实字符串的开头添加一些有趣的字符(甚至是null)。

但这就是CString的作用。但是,它的单个成员变量是一个指针,不是指向聚合结构的开头,而是指向字符数据开始处的内存位置。

因为数据字段(没有字符缓冲区)具有固定大小,所以字符串类知道如何在内存中访问它们,因为它指向char(这些字段位于指向char的指向的负偏移处) 。当然,当它释放其聚合结构时,该类还需要计算其原始内存allcation开始的位置,而不仅仅是使用其指向char的指针。

答案 1 :(得分:1)

  

printf语句(或CString)如何知道获取/返回LPCSTR运算符,从而获得指向该字符串的指针,而不是指向该对象的指针?

没有。 printf不是类型安全的。它将盲目地假装name是C字符串而不是C ++类对象。这是未定义的行为,允许程序做任何想做的事情,包括按预期工作。

要使其正常工作,您必须明确地转换对象或使用类型安全的东西,与printf不同。

如果调用似乎正常工作,CString的第一个或唯一的数据成员可能是相应类型的C字符串,其地址与对象的相同(类似于{{3} })。但是,永远不应该依赖这种行为。

答案 2 :(得分:1)

它最终出现乱码的原因是因为C风格的vararg传递没有任何类型转换。您将一个可能具有许多字段和任意布局的对象传递给一个函数,该函数在精神上执行相同的操作,并将其转换为void* - 即它会删除类型信息。如果您希望代码工作,则需要将其转换为您希望函数看到的类型。在这种情况下,您需要写:

printf("Hi %s", (LPCSTR)name);

这样实际传递给printf函数的值是指向底层字符串的指针,这是%s格式说明符所期望的。