3

我很惊讶 s 和 s2 指向“sample”的内部指针不相等,这是什么解释?

#include <string>
#include <cassert>

int main()
{
    std::string s("sample"); 

    std::string s2(std::move(s)); 

    assert(
        reinterpret_cast<int*>(const_cast<char*>(s.data())) ==
        reinterpret_cast<int*>(const_cast<char*>(s2.data()))
        ); // assertion failure here    

    return 1;
}
4

3 回答 3

3

为什么你认为它们应该是一样的?您正在使用其移动构造函数进行构建s2s这会将数据所有权从sover 转移到s2s处于“空”状态。该标准没有详细说明这意味着什么,但是在此s之后访问 's 的数据(没有先重新分配它)是未定义的。

一个非常简化(和不完整)的版本string可能如下所示:

class string {
    char* buffer;

public:

    string(char const* from)
        : buffer(new char[std::strlen(from) + 1])
    {
        std::strcpy(buffer, from);
    }

    string(string&& other)
        : buffer(other.buffer)
    {
        other.buffer = nullptr; // (*)
    }

    ~string() {
        delete[] buffer;
    }

    char const* data() const { return buffer; }
};

我希望这能说明为什么data()成员不平等。如果我们省略了标记的行,(*)我们将在末尾删除内部缓冲区两次main:一次 fors和一次 for s2。重置buffer指针可确保不会发生这种情况。

于 2013-04-07T11:03:54.977 回答
3

每个std::string都有自己指向的缓冲区。您将一个从另一个移开这一事实并不能使它共享一个缓冲区。当您初始化s2时,它会接管缓冲区s并成为该缓冲区的所有者。为了避免s“拥有”同一个缓冲区,它只是将s's 缓冲区设置为一个新的空缓冲区(s现在负责)。

从技术上讲,还涉及一些优化,很可能没有为空字符串或非常小的字符串显式分配真正的缓冲区,而是 的实现std::string将使用std::string's 本身的一部分内存。这通常称为 STL 中的小字符串优化。

另请注意,它s已被移走,因此您的代码对其数据的访问是非法的,这意味着它可以返回任何内容。

于 2013-04-07T11:04:22.937 回答
2

string在将其值替换为某个已知值之前,您不应使用 move-from :

库代码需要在参数中保留有效值,但除非类型或函数另有说明,否则对结果参数值没有其他约束。这意味着通常最明智的做法是避免再次使用 move from 参数。如果您必须再次使用它,请务必在这样做之前使用已知值重新初始化它。

该库可以将它想要的任何内容粘贴到字符串中,但很可能您最终会得到一个空字符串。这就是从 cppreference 运行示例产生的结果。然而,人们不应该期望在移出的对象中找到任何特别的东西。

于 2013-04-07T11:05:04.187 回答