字符串类成员的移动构造函数的行为

乌鸦

我知道,除非另外指定,否则所有接受rvalue引用参数的标准库函数都保证将move-from参数保留为有效但未指定状态,并且此处的某些示例可能会显示未定义的行为,但是根本的问题不依赖于此。

以下程序:

//  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:~$ 

s1移动后的输出对我来说似乎是不确定的,但是将字符串留空至少是一个可行的选择。Valgrind报告为该程序进行了一次分配,这是您期望的。

如果我做的事情非常相似,但是有班级成员,我会得到不同的结果:

//  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还报告在此进行单个分配。

为了测试他们是否真正指向同一事物,我可以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不会影响存储在中的对象c1Valgrind现在报告2个分配,这显然很必要,因为我们显然有两个不同的字符串来解释观察到的行为,但是对我来说,为什么纯粹由于更改其中之一而突然得到2个分配对我来说并不明显。如果我完全摆脱了这一举动,只是创建c1然后调用change_data()它,我只会得到您期望的1分配。

通过删除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一个动作本身似乎被零移动,而当它是类成员时却不被零移动?

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

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

编辑:仔细考虑一下,如果最终两个对象处于有效状态,则对它们两个都进行分配确实有意义。当可以确定永远不会使用其中的一种分配时,这仅仅是编译器优化了这些分配之一吗?如果有的话,构造一个最小的示例会带来恼人的危险。

MM

我认为这是由于:

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

由于对象绑定到other无法通过该引用进行更改,因此预期的移动操作的效果仅为复制。如果我删除了它,const那么行为会变回您所期望的:

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

字符串移动构造函数如何工作?

来自分类Dev

如何在默认构造函数中初始化对空字符串的类成员引用

来自分类Dev

静态向量对字符串类成员函数

来自分类Dev

从字符串创建类实例并调用构造函数

来自分类Dev

为字符串类创建复制构造函数

来自分类Dev

根据构造函数字符串文字声明std :: array成员变量的大小

来自分类Dev

为什么C ++ 11字符串新函数(stod,stof)不是字符串类的成员函数?

来自分类Dev

为什么C ++ 11字符串新函数(stod,stof)不是字符串类的成员函数?

来自分类Dev

C ++字符串-使用初始化列表构造函数时的奇怪行为

来自分类Dev

根据字符串访问类成员

来自分类Dev

根据字符串访问类成员

来自分类Dev

添加字符串类型的类成员将导致调用基类函数而不是子类

来自分类Dev

从成员函数返回字符串指针

来自分类Dev

为什么JS中的类/函数构造函数将字符串视为表达式?

来自分类Dev

ArgumentOutOfRangeException构造函数(字符串)定义

来自分类Dev

字符串与对象类型构造函数

来自分类Dev

评估从字符串构造的函数调用

来自分类Dev

Javascript 中的字符串构造函数

来自分类Dev

将字符串数组存储在类成员函数中并返回

来自分类Dev

将字符串数组存储在类成员函数中并返回

来自分类Dev

在构造函数中调整字符串成员的大小

来自分类Dev

如何在Arduino的C ++类中的构造函数中分配字符串字段?

来自分类Dev

如何使用构造函数创建C#类并使其返回字符串?

来自分类Dev

类返回null而不是分配给其构造函数的字符串属性?

来自分类Dev

如何使用构造函数创建C#类并使其返回字符串?

来自分类Dev

使用构造函数时字符串不会出现在类中

来自分类Dev

动态创建的对象(提供其类名作为字符串)不调用其构造函数

来自分类Dev

如何在类构造函数中设置字符串数组默认值

来自分类Dev

C ++ 03通过构造函数将向量移动到类成员中(移动语义)

Related 相关文章

  1. 1

    字符串移动构造函数如何工作?

  2. 2

    如何在默认构造函数中初始化对空字符串的类成员引用

  3. 3

    静态向量对字符串类成员函数

  4. 4

    从字符串创建类实例并调用构造函数

  5. 5

    为字符串类创建复制构造函数

  6. 6

    根据构造函数字符串文字声明std :: array成员变量的大小

  7. 7

    为什么C ++ 11字符串新函数(stod,stof)不是字符串类的成员函数?

  8. 8

    为什么C ++ 11字符串新函数(stod,stof)不是字符串类的成员函数?

  9. 9

    C ++字符串-使用初始化列表构造函数时的奇怪行为

  10. 10

    根据字符串访问类成员

  11. 11

    根据字符串访问类成员

  12. 12

    添加字符串类型的类成员将导致调用基类函数而不是子类

  13. 13

    从成员函数返回字符串指针

  14. 14

    为什么JS中的类/函数构造函数将字符串视为表达式?

  15. 15

    ArgumentOutOfRangeException构造函数(字符串)定义

  16. 16

    字符串与对象类型构造函数

  17. 17

    评估从字符串构造的函数调用

  18. 18

    Javascript 中的字符串构造函数

  19. 19

    将字符串数组存储在类成员函数中并返回

  20. 20

    将字符串数组存储在类成员函数中并返回

  21. 21

    在构造函数中调整字符串成员的大小

  22. 22

    如何在Arduino的C ++类中的构造函数中分配字符串字段?

  23. 23

    如何使用构造函数创建C#类并使其返回字符串?

  24. 24

    类返回null而不是分配给其构造函数的字符串属性?

  25. 25

    如何使用构造函数创建C#类并使其返回字符串?

  26. 26

    使用构造函数时字符串不会出现在类中

  27. 27

    动态创建的对象(提供其类名作为字符串)不调用其构造函数

  28. 28

    如何在类构造函数中设置字符串数组默认值

  29. 29

    C ++ 03通过构造函数将向量移动到类成员中(移动语义)

热门标签

归档