問題描述
printf 和自定義類 (printf and custom class)
I have my own class that represents a custom string class. I'm using VS2012RC. I have overloaded some operators of my class CustomString.
Here's some code:
CustomString::CustomString(string setstr)
{
str = setstr;
}
CustomString::operator const char *()
{
return (this‑>str.c_str());
}
CustomString &CustomString::operator = (char *setstr)
{
str = setstr;
return *this;
}
I can define my object and use it like this:
CustomString str = "Test string";
and i can print the result as:
printf(str);
printf((string)(str).c_str());
printf((string)(str).data());
printf("%s\n",(string)(str).c_str());
printf("%s\n",(string)(str).data());
And there is not any error.
But if i use it like this:
printf("%s\n", str);
There is an exception in msvcr110d.dll (error in memory access)
Why printf(str) is ok, but printf("%s\n",str) is not ok?
How can i modify my code to use printf("%s\n",str) ?
...
After hours of googling, I found that explict cast (string), static_cast (str) and _str() method are add a null‑terminated chars: '\0';
i've modified my code as:
printf("%s\n",str + '\0');
and it's worked!
Is there any way to modify my custom constructor to add a null‑terminated string and pass a correct value with null‑terminated chars to get working the following code:
printf("%s\n",str);
‑‑‑‑‑
參考解法
方法 1:
You can't (at least not in a portable way). printf
looks at the object passed as parameter and treats it as a %s
, which is a char array. You run into undefined behavior. Also, the parameters passed to printf
are, sort of say, type‑less.
Why printf(str) is ok?
Because the first parameter is types, and is a const char*
. The implicit cast is made via your operator. The rest of the parameters don't behave the same.
I'd use cout
instead, and overload operator << (ostream&, const CustomString&)
.
Don't do this:
I said you can't, in a portable way. For a class like
class CustomString
{
char* str;
//...
};
that might work, because of how classes are represented in memory. But, again, it's still undefined behavior.
方法 2:
Don't use printf
, its more C‑like than C++. Instead, use iostream
s, which provide a facility for you to format your own custom classes and send the to a file or stdout.
Here's a quick (untested) example that might work for you:
std::ostream& operator<< (std::ostream &os, const CustomString& str)
{
os << str.data();
return os;
}
and you'd print your custom string to stdout by doing something like
CustomString str;
// put some text in the custom string, then:
std::cout << str << std::endl;
方法 3:
printf is defined as
int printf(char const *fmt, ...)
passing a class or structure to a ... argument list has undefined behaviour and may work or crash, or just do something random (I've seen all 3) depending on the class and the compiler.
printf(str)
requires a char *, and the compiler finds you have an appropriate casting operator, so it invokes it. Note that this is pretty dodgy as you have no idea if str might or might not have a % in it.
So, you to do want printf("%s", str)
but as you have said, that doesn't work. Some compilers will give you a warning (though 'warning: This will crash' as produced by gcc isn't, in my opinion, terribly well thought out), so you have to force it to be cast to a string. So, your best solution is to explicitly cast it yourself,
printf("%s", static_cast<char const *>(str));
I'm not sure how much code all of the examples you've got there would require, as most of them are going to involve constructing a std::string from your custom string, then outputting it, then deleting the std::string.
方法 4:
You have to use printf("%s\n", str.c_str());
. %s
expects a char array and you gave it a CustomString
object which is something different. You have to get char array from the string by calling c_str()
function.
(by user1616375、Luchian Grigore、Rook、Tom Tanner、Adam Sznajder)