【问题标题】:Why std::string works after the initial value has been destroyed?为什么 std::string 在初始值被破坏后起作用?
【发布时间】:2018-08-06 12:16:08
【问题描述】:

std::string 损坏的示例导致范围及其值结束:

#include <bits/stdc++.h>

const int StackSize = 2137;

char stack[StackSize];

template <typename Type>
Type& push(Type value) {
    char *ptr = (char*)&value;
    std::copy_n(ptr, sizeof(Type), stack);
    return *(Type*)stack;
}

template <typename Type>
Type& create(Type value) {
    auto &var = push<Type>(value);
    std::cout << "Should Be: " << var << '\n';
    return var;
}

int main() {
    auto &x = create<std::string>("Hello");
    std::cout << "Is: " << x << '\n';
}

当我更改函数 push 以接收指针时,输出是正确的,但目前它是“b”

我知道 std::string 有一个指向 c-string 的指针,并且输出不同,因为函数 push 的参数在作用域结束时被破坏。

但是为什么当我传递一个指针来推送时,输出仍然是正确的? c-string 应该在 create 结束后销毁。

【问题讨论】:

  • 请显示替代代码而不是描述它。
  • push... 中有太多未定义的行为
  • 你想用这段代码实现什么?听起来像是XY problem

标签: c++ pointers casting scope stdstring


【解决方案1】:

因为您试图通过类似 C 的 memcpy/copy_n 将非平凡对象 (std::string) 存储在字符缓冲区中。

显然失败了,因为你只是在那里逐字节复制它,所以它的数据指针指向与临时value字符串相同的位置,然后,当数据被value析构函数释放时(至少如果你的编译器不做 SSO),你调用 UB 试图通过你的堆栈访问它。

我不清楚为您“工作”的代码的外观如何,但我想它只是通过在堆上分配字符串而不是删除它来避免问题。

考虑研究它是如何在 C++ 中完成的(即placement new)。

【讨论】:

  • 我猜它是由于 SSO 而“工作”的,并且会因为更长的字符串而惨遭失败
  • 那个“工作”代码只是传递一个指针来推送。为什么这有什么不同?临时字符串应该在创建后销毁,而不是推送,但在 main 中它仍然有效。
  • @Kulomb 如果我将push 更改为接受Type *value,那么代码将无法编译,所以我认为除了“简单地传递一个指针”之外还有更多的区别。我只能推测它们是什么。
  • 步骤为:char ptr = (char)value 和 push(&value)
  • @Kulomb 这样 std::string 在main 中创建,其析构函数在main 的末尾运行。因此,通过使用指针,您可以避免额外的字符串复制,并且只“存储”第一个恰好“存活”的字符串,直到程序结束。
猜你喜欢
  • 1970-01-01
  • 2016-05-10
  • 2020-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多