首页 > 解决方案 > 使用对象和移动语义的向量 push_back 方法

问题描述

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

#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 << "Move constructor called." << std::endl;控件转到复制构造函数,我注意到(我希望我没有错)他复制了Larry.

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

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

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

标签: c++oopvector

解决方案


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

活生生的例子

您只会看到 3 个移动 ctor 被称为:

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

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

当向量增长时如何强制执行移动语义?

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

        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


推荐阅读