【问题标题】:Vector push_back method working with objects and move semantics使用对象和移动语义的向量 push_back 方法
【发布时间】:2020-11-11 11:03:52
【问题描述】:

我在理解与移动语义和对象一起使用的向量的概念时遇到了问题。我把代码放上来,把问题准确地解释给你,因为我很想了解这背后的逻辑。

#include <iostream>
#include <vector>
#include <cstring>
#include "Mystring.h"
            
using namespace std;
    
class Mystring {
    private:
        char* str;
    public:
        Mystring();
        Mystring(const char* s);
        Mystring(const Mystring& source);
        Mystring(Mystring&& source);
        ~Mystring();
            
        Mystring& operator=(const Mystring& rhs);
        Mystring& operator=(Mystring&& rhs);
            
        void display() const;
        int get_lenght() const;
        const char* get_str() const;
};
    
int main() {
    vector <Mystring> stooges_vec;
    stooges_vec.push_back("Larry"); //11
    stooges_vec.push_back("Moe"); //12
    stooges_vec.push_back("Curly"); //13
            
    return 0;
}
          
//One-args constructor
Mystring::Mystring(const char* s)
    : str{ nullptr } {
    if (s == nullptr) {
        str = new char[1];
        *str = '\0';
    }
    else {
        str = new char[std::strlen(s) + 1];
        std::strcpy(str, s);
    }
}

//Copy constructor
Mystring::Mystring(const Mystring& source)
    :str{ nullptr } {
    str = new char[std::strlen(source.str) + 1];
    std::strcpy(str, source.str);
}
        
//Move constructor
Mystring::Mystring(Mystring&& source)
    :str{ source.str } {
    source.str = nullptr;
    std::cout << "Move constructor called." << std::endl;
}

//Destructor
Mystring::~Mystring() {
    delete[] str;
}
        
//Copy assignment
Mystring& Mystring::operator=(const Mystring& rhs) {
    std::cout << "Copy assignment called." << std::endl;
    if (this == &rhs)
        return *this;
    delete[] str;
    str = new char[std::strlen(rhs.str) + 1];
    std::strcpy(str, rhs.str);
    return *this;
}
        
//Move assignment
Mystring& Mystring::operator=(Mystring&& rhs) {
    std::cout << "Move assignment called." << std::endl;
    if (this == &rhs)
        return *this;
    delete[] str;
    str = rhs.str;
    rhs.str = nullptr;
    return *this;
}

所以,问题是我不明白这个程序的过程。我拿了调试器,一切都很好:

  1. 对于我要添加的第一个对象,首先调用“一个 args 构造函数”,因此我创建了我的对象,然后调用了“移动构造函数”,因此我窃取了数据并将原始对象的指针设为空。我完成它,将我的对象推入向量中,然后销毁现在包含空指针的原始对象。

  2. 在这里我永远迷路了,因为过程看起来一样,除了在std::cout &lt;&lt; "Move constructor called." &lt;&lt; std::endl; 行之后,控件转到复制构造函数,我注意到(我希望我没有错)他复制了@987654324 @。

问题是:我不知道编译器为什么要复制Larry。它就在那里,所以编译器不能简单地创建Moe,使用移动语义并将Moe 推到向量的后面吗?为什么一定要复制?

另外,对我来说,一个非常奇怪的行为是我看到在 Larry 的复制构造函数之后调用了一个析构函数,因为我看到它被破坏了。我的意思是,此时编译器正在破坏什么?原来的Larry 所以它保留了副本,或者那里到底发生了什么?

有人可以向我解释那一秒 push_back() 到底发生了什么吗?

【问题讨论】:

  • 试试这个,但使用 emplace_back 而不是 push_back
  • 我也用过emplace_back,情况也是一样。关键是我不理解在第二个 push_back 上调用的复制构造函数,它也是 emplace_back 的相同行为。
  • 看来这是罪魁祸首stackoverflow.com/questions/8001823/…
  • 离题,但你的赋值运算符不是异常安全的。你先delete[]new[]

标签: c++ oop vector


【解决方案1】:

实际上,如果您在 ideone 上运行代码:

Live example

您只会看到调用了 3 个移动 ctor:

Move constructor called.
Move constructor called.
Move constructor called.

但这取决于编译器中使用的std::vector 的特定实现。您的似乎在插入第一个元素之后和添加第二个元素之前重新分配空间,因此std::vector 必须使用复制 ctor 将第一个对象复制到新位置并销毁旧对象。由于您没有移动 ctor noexcept,它无法移动该对象。可在此处找到更多详细信息:

How to enforce move semantics when a vector grows?

要验证这是原因,只需在向量构造后添加一行:

        vector <Mystring> stooges_vec;
        stooges_vec.reserve( 3 );
        stooges_vec.push_back("Larry"); //11
        stooges_vec.push_back("Moe"); //12
        stooges_vec.push_back("Curly"); //13

这将防止重新分配 btw 对象插入。当然,要完全解决您的问题,请让您的移动 ctor 和赋值运算符 noexcept

【讨论】:

  • 我不想被讨厌,我尝试一步一步地学习 C++,所以我不会例外。谈到重新分配,我注意到在我的程序创建了第一个对象之后,在对第二个对象做同样的事情之后,编译器开始制作一个 Larry 的副本,然后他会破坏原来的 Larry,将副本放入向量中。拉里原件在一个地址,副本在另一个地址。向量创建内部当前对象的副本,以便在他扩展时重新分配它的内存,以便将它们放回去?我的想法是否正确?
  • 唐,此时您可能不想了解太多关于异常的知识。但是,要了解使用 STL 的移动语义,您需要了解为什么要使用 noexcept:stackoverflow.com/questions/26922113/…
猜你喜欢
  • 1970-01-01
  • 2017-01-19
  • 1970-01-01
  • 2014-09-13
  • 2013-09-06
  • 2021-07-24
  • 1970-01-01
  • 1970-01-01
  • 2016-11-16
相关资源
最近更新 更多