问题
I wanted to write a function that'll be cross platform (win32 & linux), and return a string representation of the datetime [hh:mm:ss dd-mm-yyyy].
Knowing that I just want to use the returned string as a temporary in a stream fashion as below:
std::cout << DateTime() << std::endl;
I considered writing a function with the following prototype
const char* DateTime();
If you return a character array, you must delete it once you're done. But I just want a temporary, I don't want to have to worry about de-allocating the string.
So I've written a function that just returns an std::string:
#include <ctime>
#include <string>
#include <sstream>
std::string DateTime()
{
using namespace std;
stringstream ss;
string sValue;
time_t t = time(0);
struct tm * now = localtime(&t);
ss << now->tm_hour << ":";
ss << now->tm_min << ":";
ss << now->tm_sec << " ";
ss << now->tm_mday + 1 << " ";
ss << now->tm_mon + 1 << " ";
ss << now->tm_year + 1900;
sValue = ss.str();
return sValue;
}
I realize that I'm returning a copy of the stack variable in DateTime. This is inefficient in that we create the string on the DateTime stack, populate it, then return a copy and destroy the copy on the stack.
Has the c++11 move-semantics revolution done anything to resolve this inefficiency - can I improve upon this?
回答1:
lapin, your code is fine C++11 code. In C++98/03 your code will probably be efficient due to compiler optimizations, but those optimizations aren't guaranteed. In C++11, those same optimizations will probably still make your return free, but just in case they don't, your string will be moved instead of copied.
So return by value guilt-free! :-)
Minor nit:
It is best practice to declare your values at the point of first use, instead of at the top of a block:
string sValue = ss.str();
return sValue;
Or perhaps even:
return ss.str();
But this is just a minor nit. Your code is fine and efficient.
回答2:
Another way to do this is to make it a function object with a stream inserter, as in:
struct DateTime()
{
friend std::ostream& operator<<(std::ostream& os, DateTime)
{
time_t t = time(0);
struct tm * now = localtime(&t);
os << now->tm_hour << ":";
os << now->tm_min << ":";
os << now->tm_sec << " ";
os << now->tm_mday + 1 << " ";
os << now->tm_mon + 1 << " ";
os << now->tm_year + 1900;
return os;
}
// Could be converted to a static method,
// since DateTime has no internal state
std::string str() const
{
// the following 3 lines can be replaced by
// return boost::lexical_cast<std::string>(*this);
std::ostringstream ss;
ss << *this;
return ss.str();
}
operator std::string() const
{ return str(); }
};
回答3:
Ok, I know this is not thread safe and all that and I'll probably be downvoted to hell end back, but I've seen the following in a library that I'm using (CERN's ROOT):
const char * myfunc(){
static std::string mystr;
/*populate mystr */
return mystr.c_str();
}
This only works if you know that no one will ever be so stupid as to hold on to the pointer.
This is a way of having a temporary that will not leak no matter what.
回答4:
In a world without RVO/NRVO this should avoid the copy construction in a pre C++11 standard library. In a post C++11 library with move constructor for string it still avoids the move constructor being called; it's probably a trivially small difference but still the OP was asking how to do better.
(And yes I agree the inheriting from string is ugly but it does work.)
#include <ctime>
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
class DateString : public string {
public:
DateString() : string() {
stringstream ss;
time_t t = time(0);
struct tm * now = localtime(&t);
ss << now->tm_hour << ":";
ss << now->tm_min << ":";
ss << now->tm_sec << " ";
ss << now->tm_mday + 1 << " ";
ss << now->tm_mon + 1 << " ";
ss << now->tm_year + 1900;
append(ss.str());
}
};
int main()
{
cout << DateString() << endl;
return 0;
}
来源:https://stackoverflow.com/questions/11269837/returning-a-string-from-a-function