[英]Ref counted smart pointer's assignment operator
尽管那里有智能指针问题的海洋,我似乎又被困了一个。 我正在尝试实现一个ref计数的智能指针,但是在以下情况下尝试使用ref计数是错误的。 评论是我认为应该是正确的引用计数。
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
来实现此目的。
为原始指针类型定义一个赋值运算符。 否则,它将构造一个引用计数为1的新智能指针,然后将其增加为2。
例:
template <typename U>
Sptr<T> &operator=(U *p)
{
if (_ptr != 0)
{
_ptr = p;
_rc = new int(1);
}
return *this;
}
众所周知,这些东西很棘手,可能还有更多的错误等待着。 我会使构造函数显式,以防止其他无意的结构。
从我所看到的代码中,您需要在智能指针类中使用适当的析构函数来调用release
并修复计数器。 您至少需要对计数器进行修改才能正常工作。
我没有在你的代码中看到一个Sptr(T *)
构造函数,你省略了吗?
作为旁注,我可能会使用std::pair
来存储计数器和T
指针,但是您的数据布局可以正常工作。 正如另一个答案指出的那样,你应该注意自我分配案例。
这是您的数据布局和界面选择后的最小实现:
#include <iostream>
#include <string>
using namespace std;
template <typename T>
class Sptr {
protected:
T *_ptr;
int *_rc;
virtual void retain() {
if (_rc) // pointing to something
++(*_rc);
clog << "retain : " << *_rc << endl;
}
virtual void release() {
if (_rc) {
--(*_rc);
clog << "release : " << *_rc << endl;
if (*_rc == 0) {
delete _ptr;
_ptr = NULL;
delete _rc;
_rc = NULL;
}
}
}
public:
Sptr() : _ptr(NULL), _rc(NULL) {} // no reference
virtual ~Sptr() { release(); } // drop the reference held by this
Sptr(T *p): _ptr(p) { // new independent pointer
_rc = new int(0);
retain();
}
virtual Sptr<T> &operator=(T *p) {
release();
_ptr = p;
_rc = new int(0);
retain();
return *this;
}
Sptr(Sptr<T> &o) : _ptr(o._ptr), _rc(o._rc) {
retain();
}
virtual Sptr<T> &operator=(Sptr<T> &o) {
if (_rc != o._rc){ // different shared pointer
release();
_ptr = o._ptr;
_rc = o._rc;
retain();
}
return *this;
}
};
int main(){
int *i = new int(5);
Sptr<int> sptr1(i);
Sptr<int> sptr2(i);
Sptr<int> sptr3;
sptr1 = sptr1;
sptr2 = sptr1;
sptr3 = sptr1;
}
_rc
是类中的指示符,指针与另一个实例共享或不共享。
例如,在此代码中:
B *b = new B;
Sptr<B> sptr1(b);
Sptr<B> sptr2(b);
sptr1
和sptr2
不共享指针,因为赋值是分开完成的,因此_rc
应该是不同的,这在我的小实现中实际上就是这种情况。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.