template<typename T>
class smart_pointer{
T* pointer;
std::size_t *refs;
void clear(){
if (!--*refs){
delete pointer;
delete refs;
}
}
public:
smart_pointer(T* p = NULL)
: pointer(p), refs(new std::size_t(1))
{}
smart_pointer(const smart_pointer<T>& other)
: pointer(other.pointer), refs(other.refs)
{
++*refs;
}
~smart_pointer(){
clear();
}
smart_pointer<T>& operator=(const smart_pointer<T>& other){
if (this != &other){
clear();
pointer = other.pointer;
refs = other.refs;
++*refs;
}
return *this;
}
smart_pointer<T>& operator=(T* p){
if (pointer != p){
pointer = p;
*refs = 1;
}
return *this;
}
T& operator*(){
return *pointer;
}
const T& operator*() const{
return *pointer;
}
T* operator->(){
return pointer;
}
const T* operator->() const{
return pointer;
}
std::size_t getCounts(){
return *refs;
}
};
我已经在valrind及其清洁条件下对其进行了测试。另外,要了解“智能”会使事情变慢的程度,我进行了以下测试:
:(使用
clang++ -O3
编译的代码)struct foo{
int a;
};
template<typename pointer_t>
class bar{
pointer_t f_;
public:
bar(foo *f)
:f_(f)
{}
void set(int a){
f_->a = a;
}
};
int main()
{
foo *f = new foo;
typedef smart_pointer<foo> ptr_t;
// typedef boost::shared_ptr<foo> ptr_t;
// typedef foo* ptr_t;
bar<ptr_t> b(f);
for (unsigned int i = 0; i<300000000; ++i)
b.set(i);
// delete f;
return 0;
}
我的实现似乎与原始指针一样快地运行,我认为这是一个好兆头。这里让我担心的是为什么Boost运行缓慢。我在实现中错过了一些重要的东西,以后可能会遇到麻烦吗?
#1 楼
所以这背后的想法主要是教育性的...
很酷。尝试并了解事物的内幕工作原理总是很有益的。
...但如果结果还不错,我什至可以考虑在现实中使用它。
请重新考虑一下。智能指针实现很难正确地实现。拥有有效C ++声誉的Scott Myers著名地尝试实现
shared_ptr
。在收到类似10次迭代的反馈后,它仍然是错误的。让我们在这里进行一些操作。
smart_pointer(T* p = NULL)
: pointer(p), refs(new std::size_t(1))
explicit
避免通过构造进行隐式类型转换。您的
operator=(T* p)
泄漏。这是一个小例子:int main()
{
int *x = new int(5);
{
smart_pointer<int> sp(x);
sp = new int(6);
} // sp destroyed here
std::cout << *x << "\n"; // This should have been deleted but wasn't
}
要说服自己,请修改
clear()
方法:void clear(){
if (!--*refs){
std::cout << "deleteting " << *pointer << " at " << pointer << "\n";
delete pointer;
delete refs;
}
}
这将打印出“在
<some memory address>
处删除6”。 这里只是
boost::shared_ptr
(或std::shared_ptr
)提供的内容的一小部分,而这里却不存在: Deleter
。允许从
delete
构造的构造函数,其中<template Tp1> shared_ptr(Tp1 * ...)
可转换为Tp1
类型。复制构造函数,允许从
T
构造可转换模板类型shared_ptr
。 使用
<T1>
和weak_ptr
复制构造函数。将构造函数和赋值运算符移动。
将
unique_ptr
用于需要的地方。nothrow
,它将替换当前的受管理对象。所有非成员运算符,例如
reset
,operator<
等,它们允许容器和算法正常工作。例如,operator==
不能与您的指针类一起正常工作,std::set
之类的东西也不会正常工作。 std::sort
的专业化。std::swap
的专业化。您的指针类无法与std::hash
或std::unordered_set
一起正常使用。 用于
std::unordered_map
测试的显式运算符bool转换,以便可以编写nullptr
。创建
if (<some_shared_ptr>) { ... }
时应经常使用诸如std::make_shared
的实用函数。 shared_ptr
或boost::shared_ptr
变慢的原因是它们是线程安全的。当您的std::shared_ptr
被多个线程访问时,可以很容易地删除它持有的指针,然后再处理另一个类,从而导致对smart_ptr
d对象的取消引用,从而导致不确定的行为。此外,所有delete
操作都专用于atomic
。上面的内容并不详尽。
我写这个并不是为了劝阻您,而只是尝试并强调这样的事实:编写类似的东西需要很多工作。这是非常技术性的,而且真的很容易出错。就标准库而言,
shared_ptr
可能是正确编写最困难的事情之一。评论
\ $ \ begingroup \ $
我认为我们应该举行“每个人都写一个共享指针日”。
\ $ \ endgroup \ $
–user1095108
2013年9月5日在9:05
\ $ \ begingroup \ $
我想补充一点,OP的实现将堆中的引用计数器与主题数据分开分配。这可以(如果经常使用)是内存碎片的来源,通常最好避免小堆分配,更不用说分配4个字节的开销就像32个字节之类。同样,如果主题数据和引用计数器将在结构中一起分配,将获得更好的数据局部性和改进的缓存行为。这两个原因是std :: make_shared <>()存在的原因,并且是创建智能指针的首选。
\ $ \ endgroup \ $
–艾米莉·L。
2014年2月3日,17:10
评论
我有一个关于增加计数的问题。如果智能指针sp最初指向其他东西,然后执行sp = sp1,该怎么办。在这种情况下,您需要先减少旧计数。