出于培训目的,我正在尝试编写自己的智能指针,模仿std::shared_ptr
。我有一个static std::map<void *, int> ref_track
来跟踪是否仍有共享指针引用内存中的某个块。
我的理念是:
template <typename PType>
class shared_ptr
{
public:
shared_ptr()
: value_(nullptr), ptr_(nullptr)
{}
template <typename T>
explicit shared_ptr(T * ptr)
: shared_ptr()
{
reset(ptr);
}
template <typename T>
shared_ptr(shared_ptr<T> const & other)
: shared_ptr()
{
reset(other.get());
}
~shared_ptr()
{
reset();
}
void reset()
{
if(value_)
{
delete value_; // Segmentation fault here!
value_ = 0;
ptr_ = 0;
}
}
template <typename T>
void reset(T * ptr)
{
reset();
if(ptr)
{
value_ = new shared_ptr_internal::storage_impl<
T
>(ptr);
ptr_ = ptr;
}
}
PType * get() const
{
return ptr_;
}
typename shared_ptr_internal::ptr_trait<PType>::type operator *()
{
return *ptr_;
}
private:
shared_ptr_internal::storage_base * value_;
PType * ptr_;
};
运行我的测试套件时,我注意到了
shared_ptr<int> a(new int(42));
a.reset(new int(13));
工作正常,但
shared_ptr<int> a(new int(42));
a = shared_ptr<int>(new int(13));
导致问题:*a
为0
而不是13
,而delete value_
在a
的析构函数中因分段错误而崩溃。我在源代码中用注释标记了崩溃。
使用的内部类是
namespace shared_ptr_internal
{
typedef std::map<void *, int> ref_tracker;
typedef std::map<void *, int>::iterator ref_tracker_iterator;
typedef std::pair<void *, int> ref_tracker_entry;
static ref_tracker ref_track;
struct storage_base
{
virtual ~storage_base() {}
};
template <typename PType>
struct storage_impl : storage_base
{
storage_impl(PType * ptr)
: ptr_(ptr)
{
ref_tracker_iterator pos = ref_track.find(ptr);
if(pos == ref_track.end())
{
ref_track.insert(
ref_tracker_entry(ptr, 1)
);
}
else
{
++pos->second;
}
}
~storage_impl()
{
ref_tracker_iterator pos = ref_track.find(ptr_);
if(pos->second == 1)
{
ref_track.erase(pos);
delete ptr_;
}
else
{
--pos->second;
}
}
private:
PType * ptr_;
};
template <typename PType>
struct ptr_trait
{
typedef PType & type;
};
template <>
struct ptr_trait<void>
{
typedef void type;
};
}
很抱歉大量的源代码,但我真的不知道如何进一步缩小它。我会很感激任何可能导致段错误的想法,以及为什么在手动使用重置时不会发生这种情况。
更新
我的(不工作)赋值运算符:
template <typename T>
shared_ptr<PType> & operator =(shared_ptr<T> const & other)
{
if(this != &other)
{
value_ = nullptr;
ptr_ = nullptr;
reset(other.get());
}
return *this;
}
答案 0 :(得分:2)
你错过了一个赋值算子。
这意味着在以下代码中:
a = shared_ptr<int>(new int(13));
创建临时共享指针;然后默认赋值运算符只是将指针复制到a
而不释放旧值或更新引用计数;然后临时删除该值,使a
留下悬空指针。
答案 1 :(得分:2)
似乎违反了rule of three:您有自定义复制构造函数和自定义析构函数,但没有自定义赋值运算符。因此,a = shared_ptr<int>(new int(13))
只会复制临时的两个指针value_
和ptr_
,而不会更新您的参考跟踪。因此,当您销毁临时文件时,不再有对该指针的跟踪引用,这将导致其被删除。另请注意,旧指针将在赋值中泄露。
答案 2 :(得分:0)
您忘记向指针类添加赋值运算符,该指针类应减少对旧对象的引用次数,并增加对指定对象的引用次数。大多数情况下,就复制d和交换功能而言,它是实现operator =的最简单方法:
shared_ptr& shared_ptr<T>::operator=( shared_ptr<T> other )
{
other.swap( this );
return *this;
}