Let me start answering this one differently.
struct Value
{
int nValue;
float fOtherValue;
};
int main()
{
Value theValue;
theValue.nValue = 220;
theValue.fOtherValue = 3.14f;
printf("Value: %d", theValue);
}
This code will print 220
without any issues. But if you pass second argument after theValue
, it wont:
printf("Values: %d, %f", theValue, theValue.fOtherValue);
Since the first variable argument theValue
doesn't meet the size specified by %d
argument. Hence, 3.14
wont (may not) be displayed. Let's not talk how printf
, argument pushing on stack, va_start
and similar things work.
Similarly, when we design a String
class this way:
struct String
{
char* pData;
public:
String()
{
pData = new char[40];
strcpy_s(pData, 40, "Sample");
}
};
And use it this way:
String str;
printf("%s", str);
It would definitely work.
But what about argument and call-stack corruption? What if size of a class (like Value
) is more than the data size (4 for Value
) as specified by format (%d
). Well, in that case, the class designed need to ensure that size of given class is same as argument-size being used by printf
function.
I wont mention the details about it, but CString
uses internal class CStringData
. The latter class holds the string, length, reference count etc. CString
(actually CSimpleStringT
) uses this class, but hold only the pointer of char
/wchar_t
, managed by CStringData
.
Largely, CString
is implemented as the String
class mentioned (as far as data and sizeof
goes).