从函数返回字符串

3
我希望编写一个跨平台的函数(win32和linux),返回日期时间的字符串表示形式[hh:mm:ss dd-mm-yyyy]。我只是想将返回的字符串作为流式传输中的临时变量使用,如下所示:
std::cout << DateTime() << std::endl;

我考虑编写一个具有以下原型的函数。
const char* DateTime();

如果你返回一个字符数组,你必须在使用完后删除它。但是我只想要一个临时的字符串,我不想担心释放它。
所以我写了一个函数,它只返回一个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;
}

我意识到在DateTime中返回堆栈变量的副本。这是效率低下的,因为我们在DateTime堆栈上创建字符串,填充它,然后返回一个副本并销毁堆栈上的副本。

c++11的移动语义革命是否对解决此效率问题有所帮助 - 我能否改进这个问题?


3
  1. NRVO(命名返回值优化)使得这个问题完全不成问题。
  2. 如果由于某种原因 NRVO 没有发挥作用,那么是移动而不是复制返回值。
- ildjarn
1
http://en.wikipedia.org/wiki/Return_value_optimization - anio
3
命名返回值优化(Named Return Value Optimization):编译器通过传递一个隐藏指针到被调用函数将要被赋值的位置,而非复制数值来从被调用函数返回至调用者。被调用函数使用该位置作为其本地变量。 - Jerry Coffin
1
就像其他人所说的那样,如果您使用C++11,RVO是有保证的。然而,我不会担心这里的瓶颈是字符串流。 - 111111
1
你应该移除 -1,因为 tm_mday 是基于 1 的。 - Alexey Frunze
显示剩余3条评论
4个回答

6
lapin,你的代码是符合C++11标准的良好代码。在C++98/03中,编译器优化可能会使你的代码高效,但这些优化并不保证。在C++11中,同样的优化可能仍然会使你的返回值免费,但为了以防万一,字符串将被移动而不是复制。
因此,放心地通过值返回! :-)
小细节:
最好在第一次使用变量时声明它们,而不是在块的顶部声明。
string sValue = ss.str();
return sValue;

也许甚至可以这样说:
return ss.str();

但这只是一个小问题。你的代码非常好,而且效率高。

几年前,我写了太多的MSSQL存储过程,开始了在代码之前声明变量的(不好?)习惯。我曾听到过你关于在第一次使用时声明变量以提高效率的观点,并且我也同意,但我仍然发现自己在代码之前声明变量 :) - fishfood

5
另一种方法是将其作为带有流插入器的函数对象,如下所示:
``` 另一种方法是将其作为带有流插入器的函数对象,如下所示: ```
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(); }
};

-1
在没有 RVO/NRVO 的情况下,这应该避免在一个早于 C++11 标准库中的复制构造。在具有字符串移动构造函数的 C++11 后期库中,它仍然可以避免调用移动构造函数;这可能只是微不足道的小差异,但 OP 仍然在询问如何做得更好。
(是的,我同意继承字符串很丑陋,但它确实有效。)
#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;
}

3
  1. std::string继承(道义上)是错误的。
  2. OP已有的代码即使没有命名返回值优化也会避免复制构造。
- ildjarn

-1

好的,我知道这不是线程安全的,可能会被狂踩,但我在使用CERN的ROOT库时看到了以下代码:

const char * myfunc(){

  static std::string mystr;

  /*populate mystr */

  return mystr.c_str();
}

只有当你知道没有人会傻到一直持有指针时,这才有效。

这是一种方法,可以拥有一个无论如何都不会泄漏的临时变量。


DeadMG,你真快!:-) 这个答案的问题在于它调用了未定义的行为。当客户端发送指向 cout 的指针时,本地的 mystr 已经删除了该指针所指向的字符数组。它可能会工作,我毫不怀疑 Simon 在发布前进行了测试。但它也可能不起作用。在多线程环境中,同一应用程序有时可能会工作,有时可能不工作。 - Howard Hinnant
3
@Howard: mystr是静态的,所以这里没有销毁它。我认为DeadMG的问题是这并不是线程安全的。 - ildjarn
@ildjarn:是的!感谢您的纠正。我完全忽略了这一点。这里的惯例是什么?删除我的评论,因为它可能会让人们感到困惑,还是保留它,让他们也可以阅读这个评论? - Howard Hinnant
@Howard:这取决于你,它可能会让人感到困惑,或者对其他读者来说,当他们阅读代码时犯了与你相同的错误,这可能会起到澄清作用。 :-] - ildjarn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接