【问题标题】:Move operator destroying original pointer移动运算符破坏原始指针
【发布时间】:2021-04-05 05:48:08
【问题描述】:

我试图有一个指向内存位置的指针。 然后,如果我修改原始分配的变量 指向相同位置的人也必须受到更改的影响。

但是,问题是,有两个函数调用是在线发生的:

a = Data("New data");
  1. Data(const char* cdata) 构造函数被调用。
  2. 调用了Data operator=(Data&& data) 移动运算符。

编辑:我知道以下内容:

  • std::shared_ptr
  • std::make_shared

但是,我正在尝试在没有这些新功能的情况下实现这一目标。我很清楚。

我的 C++ 代码:

class Data 
{
private:
    char* local_data;
    int _size = 0;
    int length(const char* c)
    {
        int i = 0;
        while(c[++i] != '\0');
        return i;
    }
public:
    Data() {
        local_data = new char[_size];
    }
    
    Data(const char* cdata){
        _size = length(cdata);
        local_data = new char[_size];
        memcpy(local_data, cdata, _size);
    }
    
    int size() { return _size; }
    char* data() { return local_data; }
    const char* data() const { return local_data; }
    
    Data& operator=(const Data& data){}
    Data& operator=(Data&& data){
        
        if(this == &data)
            return *this;
        
        _size = std::move(data.size());
        local_data = std::move(data.data());
        return *this;
    }
};

int main(){
    
    Data a("Some data");
    auto dptr = a.data(); // Gives a pointer to the original location
    a = Data("New data"); // Must modify both a and dptr
    assert(dptr == a.data()); // Should pass successfully, else fail
    return 0;
}

【问题讨论】:

  • _size 始终是 0,因此您总是分配 0 个字节并复制 0 个字节。
  • 这里的_size 部分无关紧要。自动取款机。相关的是move operator
  • “必须同时修改 a 和 dptr” - 移动语义不会发生这种情况。 dptr 是一个普通的const char*。您的移动赋值运算符实际上是用不同的值替换作为local_data 保存的指针。我什至不会尝试了解您到底想要做什么,但是可以(非常值得怀疑)的一种方法是让data()返回const char *&(即对成员的引用而不是其值的副本。请注意;这是一个糟糕的想法,也是悬空引用的秘诀。
  • @WhozCraig,谢谢。我不想要悬空指针或内存泄漏。
  • “这里的 _size 部分无关紧要。”,所以不要在 M(inital)CVE 中包含它。

标签: c++ c++11


【解决方案1】:

看来你想要std::shared_ptr<std::string>:

class Data 
{
private:
    std::shared_ptr<std::string> local_data = std::make_shared<std::string>();
public:
    Data() = default;
    
    Data(const char* cdata) : local_data(std::make_shared<std::string>(cdata)) {}
    
    int size() const { return local_data->size(); }
    char* data() { return local_data->data(); }
    const char* data() const { return local_data->c_str(); }
    
    Data operator=(const Data& rhs) {
        if (this == &data)
            return *this;
        
        *local_data = *rhs.local_data;
        return *this;
    }
};

int main(){
    
    Data a("Some data");
    auto dptr = a.data(); // Gives a pointer to the original location
    a = Data("New data"); // Must modify both a and dptr
    assert(dptr == a.data()); // Should pass successfully
}

【讨论】:

  • 嗨 Jarod42,谢谢。我不想在这里使用它,std::shared_ptr。但是,我完全清楚。让我仔细看看你在做什么。
  • 没有,std::make_shared。这怎么能以常规方式完成?
  • 常规方式是使用std提供的工具:)我什至认为std::unique_ptr就足够了(如果你不能使用std,重写更简单)。
  • 贾罗德,但没有那个。 ?
  • 如果你不能使用标准的东西,写你自己的(简化的)版本,然后你可以使用它们,而不是从头开始为每个类重新启动。如果您不知道如何写相当于std::string/std::unique_ptr/std::vector,请进行搜索,然后提出具体问题。
【解决方案2】:

您的class Data 存在一些问题。首先,它从不调用delete[] local_data,所以它会泄漏内存。添加析构函数:

~Data() {
    delete[] local_data;
}

然后确保您的移动赋值运算符正常工作。不要使用std::move(data.data()),这实际上并没有设置从移动到nullptr的对象中的指针。另外,请确保正确清理this-&gt;local_data。最简单的方法是:

Data& operator=(Data&& data){
    std::swap(local_data, data.local_data);
    std::swap(_size, data._size);
    return *this;
}

它交换指针,所以没有内存泄漏。移动后,data 的销毁将确保旧的this-&gt;local_data 被删除。

你的复制赋值运算符是空的,你应该修复它,如果你有一个移动赋值运算符,你可能还想实现一个移动构造函数。最后:

assert(dptr == a.data()); // Should pass successfully, else fail

这是错误的; dptr 不应与a.data() 相同,因为dptr 是指针的副本,该指针指向从移动分配之前获取的数据。 dptr 的值不会改变,除非您明确修改 dptr

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-23
    • 1970-01-01
    • 2013-07-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多