gpt4 book ai didi

c++ - 字符串类成员的 move 构造函数的行为

转载 作者:行者123 更新时间:2023-11-30 01:17:31 25 4
gpt4 key购买 nike

我知道,除非另有说明,否则所有接受右值引用参数的标准库函数都保证将移出的参数保留在有效但未指定的状态,并且此处的一些示例可能会表现出未定义的行为作为结果,但根本问题并不取决于此。

以下程序:

//  testmove1.cpp

#include <iostream>
#include <string>

int main() {
std::string s1{"String"};
std::cout << "s1: [" << s1 << "]" << std::endl;
std::string s2{std::move(s1)};
std::cout << "s2: [" << s2 << "]" << std::endl;
std::cout << "s1 after move: [" << s1 << "]" << std::endl; // Undefined

return 0;
}

输出:

paul@local:~$ ./testmove1
s1: [String]
s2: [String]
s1 after move: []
paul@local:~$

在 move 之后输出 s1 对我来说似乎是未定义的,但是留空的字符串至少是一个可行的选择。 Valgrind 报告正在为此程序进行一次分配,这是您所期望的。

如果我做一些非常相似的事情,但与类(class)成员一起做,我会得到不同的结果:

//  testmove2.cpp

#include <iostream>
#include <string>

class MyClass {
std::string m_data;

public:
MyClass(const std::string& data) :
m_data(data) {}
MyClass(const MyClass&& other) :
m_data{std::move(other.m_data)} {};
const std::string& get_data() const { return m_data; }
};

int main() {
MyClass c1{"Object"};
std::cout << "c1: [" << c1.get_data() << "]" << std::endl;
MyClass c2{std::move(c1)};
std::cout << "c2: [" << c2.get_data() << "]" << std::endl;
std::cout << "c1 after move: [" << c1.get_data() << "]" << std::endl;

return 0;
}

输出:

paul@local:~$ ./testmove2
c1: [Object]
c2: [Object]
c1 after move: [Object]
paul@local:~$

清除第二个字符串似乎也是一个可行的替代方案,因此这本身并不奇怪。然而,令我惊讶的是,这种行为纯粹是由于将字符串放入类中而有所不同。 Valgrind 还报告在这里进行了一次分配。

为了测试它们是否真的指向同一个东西,我可以在 move 后更改 c2,并检查 c1 是否也发生了变化:

//  testmove3.cpp

#include <iostream>
#include <string>

class MyClass {
std::string m_data;

public:
MyClass(const std::string& data) :
m_data(data) {}
MyClass(const MyClass&& other) :
m_data{std::move(other.m_data)} {};
const std::string& get_data() const { return m_data; }
void change_data() { m_data[0] = 'A'; }
};

int main() {
MyClass c1{"Object"};
std::cout << "c1: [" << c1.get_data() << "]" << std::endl;
MyClass c2{std::move(c1)};
std::cout << "c2: [" << c2.get_data() << "]" << std::endl;
std::cout << "c1 after move: [" << c1.get_data() << "]" << std::endl;

c2.change_data();
std::cout << "c1 after change: [" << c1.get_data() << "]" << std::endl;
std::cout << "c2 after change: [" << c2.get_data() << "]" << std::endl;

return 0;
}

哪些输出:

paul@local:~$ ./testmove3
c1: [Object]
c2: [Object]
c1 after move: [Object]
c1 after change: [Object]
c2 after change: [Abject]
paul@local:~$

这里,这两个对象显然不是指向同一事物,因为更改 c2 不会影响存储在 c1 中的内容。 Valgrind 现在报告 2 次分配,这似乎显然有必要解释观察到的行为,因为我们显然有两个不同的字符串,但对我来说,为什么我们突然纯粹由于更改其中一个分配而突然获得 2 次分配并不明显。如果我完全摆脱 move ,只创建 c1 然后调用 change_data(),我只会像您期望的那样获得 1 次分配。

我们可以通过在 move 后删除对 c1 的所有访问来摆脱未定义的行为(除非我有任何其他错误):

// testmove4.cpp

#include <iostream>
#include <string>

class MyClass {
std::string m_data;

public:
MyClass(const std::string& data) :
m_data(data) {}
MyClass(const MyClass&& other) :
m_data{std::move(other.m_data)} {};
const std::string& get_data() const { return m_data; }
void change_data() { m_data[0] = 'A'; }
};

int main() {
MyClass c1{"Object"};
std::cout << "c1: [" << c1.get_data() << "]" << std::endl;
MyClass c2{std::move(c1)};
std::cout << "c2: [" << c2.get_data() << "]" << std::endl;

c2.change_data();
std::cout << "c2 after change: [" << c2.get_data() << "]" << std::endl;

return 0;
}

哪些输出:

paul@local:~$ ./testmove4
c1: [Object]
c2: [Object]
c2 after change: [Abject]
paul@local:~$

我们显然无法再看到 c1 没有改变的事实,因为我们没有输出它。但是 Valgrind 仍然显示 2 个分配。

有人知道这里发生了什么吗?

  1. 为什么 std::string 单独 move 时似乎在 move 后清零,但当它是类成员时却不是?

  2. 在最后一个例子中,当我 move 一个对象然后更改它时,为什么我得到两个分配而不是一个,而我在 move 对象然后不更改它时只得到一个分配?我知道我们似乎正朝着量子计算的方向发展,但在 C++ 中发挥不确定性原理似乎还为时过早。

我使用的是 g++ 4.7.2,但我在 clang-503.0.40 上观察到相同的行为。

编辑:仔细想想,如果您最终得到两个处于有效状态的对象,那么对它们都进行分配确实有意义。这只是编译器在确定其中一个分配永远不会被使用时优化掉其中一个分配吗?如果是的话,构建最小示例的烦人危险。

最佳答案

我认为这是由于:

MyClass(const MyClass&& other) :
^^^^^

由于无法通过此引用更改绑定(bind)到 other 的对象,因此预期的 move 操作的效果只是一个拷贝。如果我删除此 const,则行为会变回您所期望的:

$ g++ -o tm3 tm3.cc -std=c++11 && ./tm3
c1: [Object]
c2: [Object]
c1 after move: []

关于c++ - 字符串类成员的 move 构造函数的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24235200/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com