复制与复制如何交换成语真的应该起作用,认真!我的代码失败 [英] How's the Copy & Swap idiom really supposed to work, seriously! My code fails
问题描述
我一直在听说这种在 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;
}
-
Main.cpp具有上面的(非交换惯用法)实现,
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:
-
首先我们有
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:
-
首先,使用
Data("New data")
创建一个新的临时对象:
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屋!