4

这是一个来自采访的问题,下面附上代码。这个功能有什么问题?

string f() {
    return "hello world";
}

对我来说,一点问题都没有,我什至可以使用这个函数运行一个程序:

#include <iostream>
#include <string>
using namespace std;

string f() {
    return "hello world";
}

int main() {
    string s2=f();
    cout<<s2<<endl;
}

这个功能有什么问题?

4

3 回答 3

6

嗯……安慰一下,在这类问题中,通常没有正确答案。您可能希望的最好结果是通过展示您的专业知识来详细说明利弊(也称为代码手淫),从而让面试官惊叹。

从风格的角度来看,使用按值返回来返回对象通常是不好的。考虑:

X f();

X x;
x = f();

f 分配一个 X。需要返回一个 X,因此在堆栈上放置了一个额外的副本作为返回值。最后,堆栈上的 x 通过赋值运算符复制到 x 中。总共有 3 个 X 出现在内存中。有人可能会说这是低效的,所以当值是一个对象时,你应该尽量不要按值返回。

然而,更精明的受访者可能会指出:

  • 一些编译器优化了临时副本。对于这种优化,我相信对 X 的赋值是通过复制构造函数还是相等运算符进行的。说“copy elison”,看看面试官是否扬眉:什么是copy elision,它是如何优化copy-and-swap 习语的?. 我猜复制省略可能在这里起作用,因为该函数位于同一个翻译单元中,因此很容易内联。
  • 几乎所有字符串类的实现都实现了写入时复制,在这种情况下,字符串文本数据的单个副本将被保存,其余数据保存在堆栈中,并且在字符串被保存时基本上不会造成开销复制。
  • 如果您使用的是早期版本的 Visual Studio,则会出现内存泄漏

那么,有什么替代方案呢?

X const& f();

可以考虑作为替代方案。但是,这有其自身的风格问题,因为调用者不清楚结果的生命周期。也许下一次调用会使先前的参考结果无效?谁知道?

void f_get( X& result );

可能是首选(一些面试官)。这有以下好处:

  • 没有局部变量的副本
  • 没有不确定生命周期的引用/指针
  • 在 X 是字符串的情况下,只保存一个文本数据的副本(尽管在所有情况下都是如此)

在这两种情况下,都牺牲了可读性——在一般情况下,调用者现在必须更加注意哪些参数是函数参数,哪些是用来保存结果的。

在 OP 中,堆栈上字符串文字的生命周期可能也不明显。函数返回时是否从堆栈中取消分配?可能不是——它可能保存在静态内存区域中(最好检查规范以确保)。但如果不是,那么结果是什么 - 可能没有,因为字符串可能会在堆上复制 char const* ,除非你有一些奇怪的字符串实现,谁的构造函数可以用一些聪明的模板类型区分文字和非文字参数-特质魔法。

无论如何,像这样说一些废话肯定会得分,如果偏离标准的话。

另外,您的控制台输出的是 ascii、UTF-8 还是 unicode?该程序可能对其中一个错误,并且在没有说英语的语言环境中也是错误的。

您是否检查了标准输出是否具有有效值,或者您是否在 Windows 中没有 -D_CONSOLE 进行编译,或者对于标准输出不可用且您必须重定向到日志库(或崩溃)的某些嵌入式设备或游戏控制台?

嗯……随便挑吧。

临时副本可能是他们正在寻找的机器人。因为在字符串的情况下临时是没有意义的(可能还有复制省略),面试官闻起来有接骨木浆果的味道,他的母亲是山羊

于 2013-11-01T02:42:34.520 回答
4

这个功能本身并没有什么问题——它是完全安全的。但是,由于此函数始终返回相同的字符串,因此您可以考虑将函数替换为常量,如下所示:

static const string kHelloWorld = "hello world";

cout << kHelloWorld << endl;

这样做的好处是这个字符串只有一个副本,现在它是一个命名常量,这意味着它可以根据需要进行更改,并且没有函数调用和返回的开销。

希望这可以帮助!

于 2013-11-01T02:12:08.223 回答
2
string f() {
    return "hello world";
}

根据 templatetypedef 的回答,这将导致在每个使用点临时构建一个字符串。

您可以通过以下方式解决此问题:

const std::string& f()
{
    static const std::string hw("Hello world");
    return hw;
}

这样,多个调用会产生对单个 std::string 实例的引用,该实例是在第一次调用期间创建的。每次调用都可能会产生性能成本,同时f()它会检查是否已经创建了本地静态 - 我不确定编译器对此的最新技术是什么。

相比之下,如果您有客户端代码直接使用常量...

static const std::string hw("Hello world");

...然后,如果您想开始返回运行时确定的值(例如,在基于环境变量的语言中选择“Hello world”消息),则必须让每个客户端更新他们的代码。如果您坚持使用该函数,那么您可以选择几个常量中的哪一个来返回引用,或者您可以将返回类型从 by-const-reference 更改为 by-value 并返回任何动态生成的文本。

于 2013-11-01T02:45:51.233 回答