【问题标题】:Heap allocations in string class in C++C ++中字符串类中的堆分配
【发布时间】:2021-03-14 17:52:24
【问题描述】:

在下面的 C++ 代码中,应该有三个堆分配,就像在 swap() 函数中一样,还创建了一个临时字符串对象。为什么这段代码中只有两个堆分配?

不使用移动语义

#include <iostream>
#include <unordered_map>

using namespace std;


static uint32_t allocations = 0;

void *operator new(size_t size)
{
    allocations++;
    cout << "Allocating " << size << " bytes\n";
    return malloc(size);
}


void swapUsingMove(string& arg1, string& arg2)
{
    string temp = arg1;
    arg1 = arg2;
    arg2 = temp;
    cout << allocations << endl;
}

int main()
{
    string str1{"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
    string str2{"ZYXWVUTSRQPONMLKJIHGFEDCBA"};

    swapUsingMove(str1, str2);
    cout << str1 << " " << str2;
    return 0;
}

输出

Allocating 51 bytes
Allocating 51 bytes
2
ZYXWVUTSRQPONMLKJIHGFEDCBA ABCDEFGHIJKLMNOPQRSTUVWXYZ

通过使用移动语义

#include <iostream>
#include <unordered_map>

using namespace std;


static uint32_t allocations = 0;

void *operator new(size_t size)
{
    allocations++;
    cout << "Allocating " << size << " bytes\n";
    return malloc(size);
}


void swapUsingMove(string& arg1, string& arg2)
{
    string temp = move(arg1);
    arg1 = move(arg2);
    arg2 = move(temp);
    cout << allocations << endl;
}

int main()
{
    string str1{"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
    string str2{"ZYXWVUTSRQPONMLKJIHGFEDCBA"};

    swapUsingMove(str1, str2);
    cout << str1 << " " << str2;
    return 0;
}

输出

Allocating 51 bytes
Allocating 51 bytes
2
ZYXWVUTSRQPONMLKJIHGFEDCBA ABCDEFGHIJKLMNOPQRSTUVWXYZ

即使不使用移动语义,为什么只有两个堆分配?临时字符串在哪里分配内存?如果在这两种情况下都有两个堆分配,那么在这里使用 std::move() 有什么好处?

【问题讨论】:

  • 您能否向我们展示您实际上难以理解的程序(“不使用移动语义”)?还包括您从程序获得的实际输出,以及可能的预期输出。
  • @jtbandes 这不是关于 SSO 的,不要误导。
  • 你用的是什么编译器?首先,我使用 gcc 和 clang 得到不同的输出(都启用了优化)
  • 您是否查看了生成的代码以了解它的作用?也许编译器识别了“交换”模式并生成代码来交换指针?
  • @ArunSuryan: 什么版本的 GCC,你用什么命令行来编译?我无法在simple test cases with Godbolt 上复制;我得到了三个分配(虽然它们要小得多;27 个字节,而不是 51 个)。

标签: c++ string c++11 move swap


【解决方案1】:

您可能使用的实现符合 C++98/03 标准,并且为 std::string 实现 copy-on-write

现场演示:https://godbolt.org/z/rP7M77

如果是这种情况,您的问题不应使用c++11 标记,因为此后不允许写入时复制。 (通常改为实施 SSO,但这是另一回事。)

如果您切换到较新的 GCC,您将看到 3 个分配:https://godbolt.org/z/b5q1MM

【讨论】:

  • 那么在这两种情况下或仅在我使用移动语义的情况下都会有三个分配?
  • @ArunSuryan 您能否回答 cmets 关于 GCC 版本的问题?另外,您使用哪些编译标志?无论如何,它似乎是部分 C++11 支持。 C++11 中不允许使用 COW,而 std::move 直到 C++11 才存在。根据 C++ 标准,您不能同时使用两者。
  • 它是 gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36)
  • @ArunSuryan 为什么应该有 3 个带有移动语义的分配?移动只是“窃取”资源,即存储的字符串。为什么要分配任何东西?
  • @ArunSuryan 是的,GCC 4.8.5 似乎混合了 COW 和 std::move 功能,这与任何特定的 C++ 标准都不对应。我建议你不要使用这种过时的版本。
猜你喜欢
  • 2011-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-01
  • 2020-10-09
  • 1970-01-01
  • 1970-01-01
  • 2019-05-20
相关资源
最近更新 更多