【问题标题】:Is it good practice to make every const string be referenced?引用每个 const 字符串是一种好习惯吗?
【发布时间】:2017-08-09 06:38:50
【问题描述】:

代码 A:

string a = "Hello World";

for (int i=0; i<1000000; ++i) 
     const string b = a;

代码 B:

string a = "Hello World";

for (int i=0; i<1000000; ++i) 
     const string &b = a;

如果我分别运行 2 个代码并测量 CPU 时间,代码 B 比代码 A 快大约 2.5 倍。

除了charintfloat 等基本类型之外...我了解到获取原始引用比复制它更快。

尽管在大多数情况下差异几乎可以忽略不计,但是否可以将始终引用 const string 类型(和其他非原始类型)视为一种好习惯?

【问题讨论】:

  • 通常的指导方针如下。如果一个类型的sizeof 大于sizeof(void*),则始终通过 const 引用传递,否则通过复制传递。如果要更改对象,请传递非常量引用,而不管大小。
  • 也有例外——例如,如果你想在函数中制作参数的副本,然后按值传递——它会隐式地制作你想要的副本,并且可以利用诸如移动操作以有效地做到这一点。
  • 类可能会定义自定义的复制或赋值运算符(就像字符串一样),所以sizeof 并不总是一个很好的复制时间指标。
  • 不是“每个”,你需要确保你使用包含在被引用对象的生命周期内的引用(b)(a)
  • 你的编译器很烂。 :) 两个代码都没有任何效果;它应该减少到 ret 指令或其他任何东西。

标签: c++ string optimization types reference


【解决方案1】:

是的,对于复制成本高昂的任何对象,通常最好使用 const 引用。但是您必须注意原始对象在您不期望时不会发生变化;特别是您需要确保原始对象没有被破坏,因为这会导致未定义的行为。在您的玩具示例中这不是问题,但在现实世界中这是一个大问题。

使用 const 引用的最佳位置是在函数参数中。您知道传递给函数的对象在仍在函数内部时不能更改或销毁。

【讨论】:

    【解决方案2】:

    有几件事情需要考虑。

    1. 您的用例。你想复制还是观察?
    2. 该类型是否执行深度复制语义(例如,在复制时分配动态内存)?

    如果您只想观察物体
    如果对象的类型在复制时分配动态内存,则始终通过 const 引用传递。
    否则,如果类型的sizeof 大于sizeof(void*),则通过 const 引用传递。
    否则按值传递。

    如果您希望复制对象,则只需按值传递即可。

    当然,在一个奇怪的用例中,您可能会做其他事情,但这是我看到的普遍遵循的一般准则。

    还需要考虑移动语义,但这是一个不同的问题。

    【讨论】:

    • 您可能还想提到std::stringstd::string_view 来观察C++17 中的字符串。
    • 还有一个有趣的数据点是sizeof(string_view)大约是2*sizeof(void*),但是string_view在C++17标准库中是按值传递的。
    • @BoPersson 我最近正在研究std::anylibcxxstdlibc++ 中的实现。在libcxx 中,他们认为“小对象”是3*sizeof(void*),而在stdlibc++ - sizeof(void*) 中则相反。我觉得这很有趣。它也可以在按值传递的标准迭代器中找到。我平台上的一些容器(如 std::deque)有一个大小为 32 字节的迭代器。
    猜你喜欢
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-14
    • 1970-01-01
    • 1970-01-01
    • 2012-01-03
    • 1970-01-01
    相关资源
    最近更新 更多