尽管智能指针问题层出不穷,但我似乎又遇到了一个问题。我正在尝试实现一个引用计数的智能指针,但是当我在以下情况下尝试时,引用计数是错误的。评论是我认为应该是正确的引用计数。
Sptr<B> bp1(new B); // obj1: ref count = 1
Sptr<B> bp2 = bp1; // obj1: ref count = 2
bp2 = new B; // obj1: ref count = 1, obj2: rec count = 1 **problem**
在我的实现中,我的 obj2 引用计数是 2,因为这段代码:
protected:
void retain() {
++(*_rc);
std::cout << "retained, rc: " << *_rc << std::endl;
}
void release() {
--(*_rc);
std::cout << "released, rc: " << *_rc << std::endl;
if (*_rc == 0) {
std::cout << "rc = 0, deleting obj" << std::endl;
delete _ptr;
_ptr = 0;
delete _rc;
_rc = 0;
}
}
private:
T *_ptr;
int *_rc;
// Delegate private copy constructor
template <typename U>
Sptr(const Sptr<U> *p) : _ptr(p->get()), _rc(p->rc()) {
if (p->get() != 0) retain();
}
// Delegate private assignment operator
template <typename U>
Sptr<T> &operator=(const Sptr<U> *p) {
if (_ptr != 0) release();
_ptr = p->get();
_rc = p->rc();
if (_ptr != 0) retain();
return *this;
}
public:
Sptr() : _ptr(0) {}
template <typename U>
Sptr(U *p) : _ptr(p) { _rc = new int(1); }
// Normal and template copy constructors both delegate to private
Sptr(const Sptr &o) : Sptr(&o) {
std::cout << "non-templated copy ctor" << std::endl;
}
template <typename U>
Sptr(const Sptr<U> &o) : Sptr(&o) {
std::cout << "templated copy ctor" << std::endl;
}
// Normal and template assignment operator
Sptr &operator=(const Sptr &o) {
std::cout << "non-templated assignment operator" << std::endl;
return operator=(&o);
}
template <typename U>
Sptr<T> &operator=(const Sptr<U> &o) {
std::cout << "templated assignment operator" << std::endl;
return operator=(&o);
}
// Assignment operator for assigning to void or 0
void operator=(int) {
if (_ptr != 0) release();
_ptr = 0;
_rc = 0;
}
构造函数使用 ref count = 1 初始化,但在我的赋值运算符中,我保留了对象,使 ref count = 2。但在这种情况下它应该仅为 1,因为 bp2 = new B 只是一个指向该对象的指针。我查看了各种智能指针实现示例,但我似乎无法弄清楚它们如何处理我遇到的这种情况。
感谢您的宝贵时间!
最佳答案
无论如何,赋值运算符充满了小错误和不必要的复杂性。例如,当您分配 Sptr<T>
对它自己来说,它会产生有趣的效果。大多数手动编写的赋值运算符应该如下所示:
T& T::operator= (T const& other) {
T(other).swap(*this);
return *this;
}
...或者
T& T::operator= (T other) {
other.swap(*this);
return *this;
}
一旦有状态分配器进入游戏,事情就会发生一些变化,但我们可以在这里忽略这个细节。主要思想是利用为复制构造函数和析构函数所做的现有工作。请注意,如果右侧不是 T
,此方法也适用,即,您仍然可以利用相应的构造函数、析构函数和 swap()
.无论如何,唯一可能的额外工作是可取的,并且实现起来微不足道:swap()
成员。在您的情况下,这也非常简单:
template <typename T>
void Sptr<T>::swap(Sptr<T>& other) {
std::swap(this->_ptr, other._ptr);
std::swap(this->_rc, other._rc);
}
请注意您的赋值运算符采用 int
: 这是一个非常糟糕的主意!要重置指针,您最好使用 reset()
。方法。在 C++ 2011 中,您可以合理地使用一个采用 std::nullptr_t
的方法。不过,为了这个目的。
关于c++ - 引用计数智能指针的赋值运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13435042/