weak_ptr 可以用来干嘛

智能指针中,除了大家喜闻乐见的shared_ptr,还有一个奇葩的weak_ptr。我读到这里时,就想,这货有什么用呢,获得对象又不增加引用计数?老老实实用着shared_ptr不是挺好的?标准库里何必要搞出这样的一个东西来?

毫无疑问我错了。weak_ptr有着极为重要的作用……

从实际问题谈起

在大作业中,我遇到了这样的需求:在一个对象的成员函数内,将这个对象的shared_ptr作为参数传入另一个函数中。值得注意的是,这个对象设计为只应通过shared_ptr调用。当然这个需求不是必要的,只是为了封装的完整、易用。

在成员函数中,我只能通过this获得这个对象的普通指针,而不能直接获得它的智能指针。那么我该怎么办呢?

  • 用普通指针构造一个shared_ptr

如此一来,虽得到了一个指向对象的智能指针,但这不是我想要的智能指针——它与原有的智能指针有着完全不同的引用计数。当其中一个智能指针析构之后,另一个指针很有可能还会存在,并继续调用这个对象——显然,这会导致异常出现。这就是为什么 «C++ Primer» 会苦口婆心地告诫我们:不要混用普通指针和智能指针。

  • shared_ptr作为成员变量储存?

很好,现在解决了两个shared_ptr不同的问题,使用过程中也不会出现异常了。然而,新的问题又出现了:由于这个对象永远保有它自己的shared_ptr,其引用计数永远不会归零,它永远不会被析构掉,它会一直存在于内存中,就如同全局变量一般。这显然不是我们希望看到的。

  • 那该怎么办呢?

聪明的你一定想到了,既然我们谈的是weak_ptr的作用,那一定是用weak_ptr来解决这个问题咯。没错,通过储存这个成员的weak_ptr,我们就能很开心地随时得到这个对象的shared_ptr了。

改进

前面提到,这个对象具有一个特点:这个对象设计为只能通过shared_ptr调用。因而,为了避免勿用普通指针,造成普通指针和智能指针混用的情况,我们最好对其进行封装。首先,普通的构造函数对这个类肯定是不适用,它不能满足产生智能指针的效果。其次,应当给成员变量初始化weak_ptr。最后,应当防范取地址符获取到普通指针的情况。


代码实现

class Foo{
public:
    static std::shared_ptr<Foo> new_foo(int i);
    std::shared_ptr<Foo> operator&();
    ~Foo();

private:
    Foo(int i);
    void init_weak_pointer();

    int x;
    std::weak_ptr<Foo> thisWeakPointer;
};

std::shared_ptr<Foo> Foo::new_foo(int i){
    std::shared_ptr<Foo> sharedPointer(new Foo(i));
    sharedPointer->init_weak_pointer(sharedPointer);
    return sharedPointer;
}

std::shared_ptr<Foo> operator&(){
    std::shared_ptr<Foo> sharedPointer = thisWeakPointer.lock();
    try{
        if(sharedPointer){
            return sharedPointer;
        }
        else{
            throw std::logic_error("Foo class can only be used with shared_ptr!");
        }
    }
}

Foo::~Foo(){}

Foo::Foo(int i): x(i){}

void Foo::init_weak_pointer(std::shared_ptr<Foo> sharedPointer){
    thisWeakPointer = sharedPointer;
}

tandf

Tsinghua University, Department of Automation
One thing at a time. One thing done well.