复制与复制如何交换成语真的应该起作用,认真!我的代码失败 [英] How's the Copy & Swap idiom really supposed to work, seriously! My code fails

查看:45
本文介绍了复制与复制如何交换成语真的应该起作用,认真!我的代码失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在听说这种在 C ++ 中实现 assign operator =()的广告方式.

I have been hearing about this, very advertised way of implementing an assign operator=() in C++.

MVP,

  • https://onlinegdb.com/meN5a_I2I (Version 1)
  • https://onlinegdb.com/HybpyOO6P (Version 2)

版本2:

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

这似乎是失败的地方,

没有 Copy&的 copy operator =()交换成语:

A copy operator=() without Copy & Swap Idiom:

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具有上面的(非交换惯用法)实现,

  1. Main.cpp with above (non-swap idiom) implementation called,

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;
 }


与上述相同,但复制和复制"除外交换成语:


Same as above, except with Copy & Swap Idiom:

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& amp;的简洁易行的实现是交换成语应该在这里失败.而且,我知道确实会发生一些运行时开销.但是,通常情况下,复制和交换习惯似乎更干净,更好.

I observe, that there's clean up. But, is the clean and easy implementation of Copy & Swap Idiom supposed to fail here. And, I know that some runtime overhead does occur. But, generally, the Copy & Swap idiom seems a lot cleaner and better.

编辑:我知道自我分配问题,应该可以解决这个问题.自赋值在大多数程序中极为罕见,因此,即使支票几乎总是虚假的,显式检查也会为每项自赋值增加一小笔费用.

I am aware of the self-assignment problem, and that this is supposed to help with that. Self-assignment is exceedingly rare in most programs, so an explicit check adds a small cost to every self-assignment even though the check is almost always false.

萨特&Alexandrescu显示了以交换成员的形式编写的赋值运算符.这样可以防止自我分配,异常安全,并重新使用副本构造函数.

Sutter & Alexandrescu shows an assignment operator written in terms of a swap member. This is safe against self-assignment, exception-safe, and re-uses the copy constructor.

版本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 .

The main problem is that you seem to misunderstand how modifications to a.local_data are propagated (or not, as is what happens) to ptr.

让我们逐句陈述这一点:

Lets draw this up, statement by statement:

  1. 首先我们有

  1. First we have

Data a("Some data");

这给了我们类似的东西

+--------------+     +-------------+
| a.local_data | --> | "Some data" |
+--------------+     +-------------+

  • 然后我们复制"指向变量 ptr :

    auto ptr = a.data();
    

    这给了我们类似的东西

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

  • 在下一条语句中发生了两件事:

  • With the next statement two things happen:

    1. 首先,使用 Data("New data")创建一个新的临时对象:

    1. First a new temporary object is created with 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" |
    +------------------+     +------------+
    

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

    The temporary object is then destructed, leaving you with:

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

    此处指针 ptr 的值不再有效!

    Here the value of pointer ptr is no longer valid!

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

    In the copy-and-swap version something else happens:

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

    然后破坏临时对象,使您再次 :

    Then the temporary object is destructed leaving you again with:

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

    与以前一样, ptr 的值不再有效.

    As before the value of ptr is no longer valid.

  • 但是重要的是 ptr a.local_data 之间的断开连接.您可以修改其中一个,但不会被修改.当然哪个会导致断言失败.

    However the important thing is the disconnect between ptr and a.local_data. You can modify one, but the other will not be modified. Which of course leads to the assert to fail.

    在您的示例中,它在第一种情况下起作用是巧合,因为内存分配器似乎正在重新使用传递给 delete [] 的内存.当然,这不能保证总是发生甚至是重复发生.打破第一个工作"的一种可能方法是:例如,尝试使用 long 字符串创建一个新对象,如

    That in your example it works for the first case is a coincidence, as the memory allocator seems to be reusing the memory passed to delete[]. This is of course nothing that can be guaranteed to happen always or even repatedly. As a possible way to break the first "working" example, try creating a new object with a longer string, as in

    a = Data("Much longer string");
    

    这篇关于复制与复制如何交换成语真的应该起作用,认真!我的代码失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆