这背后的想法主要是教育性的,但如果结果是好的,我什至可以考虑在现实中使用它。这是我第一次尝试实现智能指针:

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运行缓慢。我在实现中错过了一些重要的东西,以后可能会遇到麻烦吗?

评论

我有一个关于增加计数的问题。如果智能指针sp最初指向其他东西,然后执行sp = sp1,该怎么办。在这种情况下,您需要先减少旧计数。

#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,它将替换当前的受管理对象。
所有非成员运算符,例如resetoperator<等,它们允许容器和算法正常工作。例如,operator==不能与您的指针类一起正常工作,std::set之类的东西也不会正常工作。
std::sort的专业化。
std::swap的专业化。您的指针类无法与std::hashstd::unordered_set一起正常使用。
用于std::unordered_map测试的显式运算符bool转换,以便可以编写nullptr
创建if (<some_shared_ptr>) { ... }时应经常使用诸如std::make_shared的实用函数。 shared_ptrboost::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