微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

说真的,复制和交换习语真的应该如何工作!我的代码失败

如何解决说真的,复制和交换习语真的应该如何工作!我的代码失败

我听说过这种在 C++ 中实现 assign operator=() 的非常宣传的方式。

编辑:MVP,

版本 2:

a.out: main.cpp:52: int main(): Assertion `ptr == a.data()' Failed.                                                        
Aborted (core dumped) 

这似乎是失败的地方,

没有复制和交换习语copy operator=()

Data& operator=(const Data& rhs)
{
    if(this != &data)
    {
        _size = rhs.size();
        delete[] local_data;
        local_data = new char[_size];
        std::copy(rhs.data(),rhs.data() + rhs.size(),local_data);
    }
    return *this;
}
  1. 调用上述(非交换习语)实现的 Main.cpp,

    int main(){
       Data a("Some data");
       auto ptr = a.data(); // Obtains a pointer to the original location
       a = Data("New data"); // When a is modified,this modification should effect the 'ptr'
       assert(ptr == a.data()); // Succeeds
       return 0;
     }
    

同上,除了复制和交换习语:

void swap(Data& rhs) noexcept
{
    std::swap(_size,rhs.size());
    std::swap(local_data,rhs.data());
}

Data& operator=(const Data& rhs)
{
    Data tmp(data);
    swap(data);
    return *this;
}

2:使用交换习语实现:

int main(){
    Data a("Some data");
    auto ptr = a.data(); // Obtains a pointer to the original location
    a = Data("New data"); // When a is modified,this modification should effect the 'ptr' 
    assert(ptr == a.data()); // Fail
    return 0;
}

我观察到,有清理。但是,copy & Swap Idiom 的干净和简单的实现应该在这里失败。而且,我知道确实会发生一些运行时开销。但是,一般来说,复制和交换习语似乎更简洁、更好。

编辑:我知道自我分配问题,这应该有助于解决这个问题。自赋值在大多数程序中极为罕见,因此即使检查几乎总是错误的,显式检查也会为每个自赋值增加少量成本。

Sutter & Alexandrescu 展示了一个用交换成员编写的赋值运算符。这对自赋值是安全的,异常安全,并重用复制构造函数

版本 2 的完整代码

#include <iostream>
#include <cassert>
using namespace std;

class Data 
{
private:
    char* local_data;
    int _size = 0;
    
    inline int length(const char* str)
    {
        int n = 0;
        while(str[++n] != '\0');
        return n;
    }
    
public:
    Data() {
        local_data = new char[_size];
    }
    
    Data(const char* cdata) : _size { length(cdata) }{
        local_data = new char[_size];
        std::copy(cdata,cdata + _size,local_data);
    }
    
    int size() const { return _size; }
    const char* data() const { return local_data; }
    
    void swap(Data& rhs) noexcept
    {
        std::swap(_size,rhs._size);
        std::swap(local_data,rhs.local_data);
    }
    
    Data& operator=(const Data& data)
    {
        Data tmp(data);
        swap(tmp);
        return *this;
    }
};



int main()
{
    Data a("Some data");
    auto ptr = a.data(); // Obtains a pointer to the original location
    a = Data("New data"); // When a is modified,this modification should effect the 'ptr' (char*)
    assert(ptr == a.data()); // Fails
    return 0;
}

解决方法

主要问题是您似乎误解了对 res.flatten() 的修改是如何传播(或不传播)到 a.local_data 的。

让我们逐个陈述:

  1. 首先我们有

    ptr

    这给了我们类似的东西

    Data a("Some data");
    
  2. 然后我们将指针“复制”到变量+--------------+ +-------------+ | a.local_data | --> | "Some data" | +--------------+ +-------------+ 中:

    ptr

    这给了我们类似的东西

    auto ptr = a.data();
    
  3. 在下一个语句中发生两件事:

    1. 首先使用 +--------------+ | a.local_data | --\ +--------------+ \ +-------------+ >--> | "Some data" | +-----+ / +-------------+ | ptr | -----------/ +-----+ 创建一个新的临时对象:

      Data("New data")
    2. 然后在第一个版本中,您在复制赋值运算符中使用 +--------------+ | a.local_data | --\ +--------------+ \ +-------------+ >--> | "Some data" | +-----+ / +-------------+ | ptr | -----------/ +-----+ +------------------+ +------------+ | temporary_object | --> | "New data" | +------------------+ +------------+ delete[] 会发生这种情况:

      new[]

      然后临时对象被破坏,留下:

      +--------------+     +------------+
      | a.local_data | --> | "New data" |
      +--------------+     +------------+
      
      +-----+
      | ptr | --> ???
      +-----+
      
      +------------------+     +------------+
      | temporary_object | --> | "New data" |
      +------------------+     +------------+
      

      此处指针 +--------------+ +------------+ | a.local_data | --> | "New data" | +--------------+ +------------+ +-----+ | ptr | --> ??? +-----+ 的值不再有效!

    3. 在复制和交换版本中会发生其他事情:

      ptr

      然后临时对象被破坏,让你再次

      +--------------+     +------------+
      | a.local_data | --> | "New data" |
      +--------------+     +------------+
      
      +-----+
      | ptr | ---------------\
      +-----+                 \     +-------------+
                               >--> | "Some data" |
      +------------------+    /     +-------------+
      | temporary_object | --/
      +------------------+
      

      和以前一样,+--------------+ +------------+ | a.local_data | --> | "New data" | +--------------+ +------------+ +-----+ | ptr | --> ??? +-----+ 的值不再有效。

然而,重要的是 ptrptr 之间的脱节。您可以修改一个,但另一个不会被修改。这当然会导致断言失败。

在您的示例中,它适用于第一种情况是巧合,因为内存分配器似乎正在重用传递给 a.local_data 的内存。这当然不能保证总是或什至重复发生。作为打破第一个“工作”示例的可能方法,尝试使用更长字符串创建一个新对象,如

delete[]

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。