为什么原始指针不会增加shared_ptr的引用计数?

4

shared_ptr使用引用计数来确定何时销毁对象。 请看下面的代码:

int main() {
    std::shared_ptr<int> pt = std::make_shared<int>(3);
    int *pt2 = pt.get();
    cout << "reference count " << pt.use_count() << endl;
    pt = 0;
    cout << *pt2;

};

当我将pt设置为0后,引用计数应该变为0,并且对象应该被销毁。但是我仍然可以使用pt2访问它。在我的情况下,结果是正确的,但我猜这只是幸运。那么这是否意味着如果程序员想做一些愚蠢的事情,引用计数机制仍然无法使其100%安全?


6
没错,您的分析非常正确。这是C++:你仍然可以自己给自己惹麻烦。 - Olivier Sohn
1
“智能指针”不是指针,也不是聪明的东西,更不是解决指针无能的方法。它们只是帮助在作用域结束时自动释放资源的工具。 - curiousguy
如果引用计数变为0,通过裸指针访问对象时该对象将会丢失,我很高兴它不是这样的! - arynaq
2
该库类型仅实现了管理所需的样板文件。它不能防止错误的管理。在编程中,没有任何东西可以保护免受人为因素的影响。 - StoryTeller - Unslander Monica
@OlivierSohn - 有趣的是你觉得需要用“许多”来限定,而不是“全部”,嗯? ;) - StoryTeller - Unslander Monica
显示剩余2条评论
2个回答

5
这不是shared_ptr的工作方式。当您从第一个指针生成新的共享指针时,计数会增加。get只是处理仅能使用原始指针的旧代码的方法。通过使用get,您正在破坏智能指针的安全性。
换句话说,get返回的指针仅适用于观察者,而非所有者,当您真正需要一个原始指针时才使用它。如果您只需要观察者,请使用weak_ptr
原则上,get可以假定拥有权并增加计数,但仍然不清楚何时再次减少计数。一旦提取了原始指针,就无法回到原始指针的计数器。
除非您知道自己在做什么,否则请避免使用get
以下是其工作方式:
#include<iostream>
#include<memory>

using std::cout; using std::endl;

int main(){

    std::shared_ptr<int> pt = std::make_shared<int>(3);
    assert(pt.use_count() == 1);
    std::shared_ptr<int> pt2 = pt;
    assert(pt.use_count() == 2);
    assert(pt2.use_count() == 2);
    pt.reset(); // or pt = 0;
    assert(pt.use_count() == 0);
    assert(pt2.use_count() == 1);
    assert(*pt2 == 3);
    assert(!pt);

    return 0;
}

5
在我将pt设置为0之后,引用计数应该变为0,并且对象应该被销毁。
引用计数确实变为了零,对象也被销毁了。
int替换为您自己的类MyInt,以查看构造函数和析构函数的调用...
class MyInt
{
private:
   int val;

public:
   MyInt() : val(0)          { std::cout << "default c'tor called" << std::endl; }
   MyInt(int rhs) : val(rhs) { std::cout << "c'tor (" << rhs << ") called" << std::endl; }
   ~MyInt()                  { std::cout << "d'tor called" << std::endl; }
   int getval (void)         { return val; }
};

...然后更新main()函数...

int main()
{
   std::shared_ptr<MyInt> pt = std::make_shared<MyInt>(3);

   MyInt* pt2 = pt.get();

   std::cout << "reference count " << pt.use_count() << std::endl;

   pt = 0;

   std::cout << pt2->getval() << std::endl;

   return 0;
}

输出结果可能如下所示...
构造函数(3)被调用
引用计数为1
析构函数被调用
3 可运行的示例 最后一行输出是数字三(如果确实是三),这并不意味着对象没有被删除。
对指向已删除对象的指针进行解引用是未定义的行为,因此任何事情都有可能发生。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接