[在Sean Parent https://youtu.be/W2tWOdzgXHA在33:41的C ++ Seasoning视频中,当他们开始谈论“没有原始同步原语”时,他举了一个例子来说明使用原始同步原语我们会弄错它。该示例是写类的错误副本:
template <typename T>
class bad_cow {
struct object_t {
explicit object_t(const T& x) : data_m(x) { ++count_m; }
atomic<int> count_m;
T data_m;
};
object_t* object_m;
public:
explicit bad_cow(const T& x) : object_m(new object_t(x)) { }
~bad_cow() { if (0 == --object_m->count_m) delete object_m; }
bad_cow(const bad_cow& x) : object_m(x.object_m) { ++object_m->count_m; }
bad_cow& operator=(const T& x) {
if (object_m->count_m == 1) {
// label #2
object_m->data_m = x;
} else {
object_t* tmp = new object_t(x);
--object_m->count_m; // bug #1
// this solves bug #1:
// if (0 == --object_m->count_m) delete object_m;
object_m = tmp;
}
return *this;
}
};
然后他要求观众找到错误,即他确认的错误#1。
但是我猜,一个更明显的错误是,当某个线程将要执行执行我用标签#2表示的代码行时,突然之间,其他线程只是破坏了该对象,而析构函数是调用,删除object_m
。因此,第一个线程将遇到一个已删除的内存位置。
我对吗?我似乎不是这样!
其他线程只是破坏对象,而析构函数是调用,删除object_m。因此,第一个线程将遇到删除的内存位置。
我对吗?我似乎不是这样!
您的异议并不成立,因为当时*this
指向该对象且计数为1。除非某人未正确玩此游戏,否则计数器无法达到0(但在任何情况下都可能发生) 。