使用对象并移动语义的向量push_back方法

时间:2020-07-22 00:52:00

标签: c++ oop vector

我在理解使用移动语义和对象的向量的概念时遇到问题。我将放置代码并确切地向您解释问题,因为我真的很想了解其背后的逻辑。

#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构造函数”,以便创建我的对象,然后调用“ move构造函数”,以便我窃取数据并使null的指针为空。原始对象。我完成它,将我的对象推入向量中,然后销毁现在位于其中带有空指针的原始对象。

  2. 在这里,我会永远迷失方向,因为过程看起来相同,除了在std::cout << "Move constructor called." << std::endl;行之后,控件转到了复制构造函数,我注意到(我希望我没有错)他Larry的副本。

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

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

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

1 个答案:

答案 0 :(得分:1)

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

Live example

您只会看到3个移动ctor:

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

但这取决于编译器中使用的std::vector的特定实现。您似乎在插入第一个元素之后,添加第二个元素之前重新分配了空间,因此std::vector必须使用copy 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对象插入。当然,要完全解决您的问题,请使用移动控制器和赋值运算符noexcept