gpt4 book ai didi

c++ - 说真的, copy-and-swap 习语真的应该如何工作!我的代码失败

转载 作者:行者123 更新时间:2023-12-03 18:57:42 25 4
gpt4 key购买 nike

我一直听说这个,非常宣传的实现 assign operator=() 的方式。在 C++ .
编辑:MVP(s),

  • https://onlinegdb.com/meN5a_I2I (版本1)
  • https://onlinegdb.com/HybpyOO6P (第 2 版)

  • 版本 2:
    a.out: main.cpp:52: int main(): Assertion `ptr == a.data()' failed.                                                        
    Aborted (core dumped)
    这就是它似乎失败的地方,
    一个 copy operator=()没有 copy-and-swap 习语 :
    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;
    }
  • 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;
    }

  • 同上,除了 copy-and-swap 习语:
    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 的干净和简单的实现应该在这里失败。而且,我知道确实会发生一些运行时开销。但是,一般来说,Copy & Swap 习语似乎更简洁、更好。
    编辑:我知道自我分配问题,这应该有助于解决这个问题。自赋值在大多数程序中极为罕见,因此即使检查几乎总是错误的,显式检查也会为每个自赋值增加少量成本。
    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;
    }

    最佳答案

    主要问题是你似乎误解了如何修改a.local_data被传播(或不传播,正如所发生的那样)到 ptr .
    让我们逐个陈述:

  • 首先我们有
    Data a("Some data");
    这给了我们类似的东西
    +--------------+     +-------------+
    | a.local_data | --> | "Some data" |
    +--------------+ +-------------+
  • 然后我们将指针“复制”到变量 ptr 中:
    auto ptr = a.data();
    这给了我们类似的东西
    +--------------+
    | a.local_data | --\
    +--------------+ \ +-------------+
    >--> | "Some data" |
    +-----+ / +-------------+
    | ptr | -----------/
    +-----+
  • 在下一条语句中,发生了两件事:
  • 首先创建一个新的临时对象 Data("New data") :
    +--------------+
    | 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 | --> ???
    +-----+
    这里指针的值ptr不再有效!
  • 在 copy-and-swap 版本中,还会发生其他事情:
    +--------------+     +------------+
    | a.local_data | --> | "New data" |
    +--------------+ +------------+

    +-----+
    | ptr | ---------------\
    +-----+ \ +-------------+
    >--> | "Some data" |
    +------------------+ / +-------------+
    | temporary_object | --/
    +------------------+
    然后临时对象被破坏,让你再次:
    +--------------+     +------------+
    | a.local_data | --> | "New data" |
    +--------------+ +------------+

    +-----+
    | ptr | --> ???
    +-----+
    与之前的值 ptr 相同不再有效。


  • 然而重要的是 ptr之间的脱节和 a.local_data .您可以修改一个,但另一个不会被修改。这当然会导致断言失败。
    在您的示例中,它适用于第一种情况是巧合,因为内存分配器似乎正在重用传递给 delete[] 的内存。 .这当然不能保证总是或什至重复发生。作为打破第一个“工作”示例的可能方法,尝试使用更长的字符串创建一个新对象,如
    a = Data("Much longer string");

    关于c++ - 说真的, copy-and-swap 习语真的应该如何工作!我的代码失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65489236/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com