为了账号安全,请及时绑定邮箱和手机立即绑定

为什么std :: shared_ptr <void>工作

为什么std :: shared_ptr <void>工作

C++
一只萌萌小番薯 2019-11-25 14:47:31
我发现一些代码使用std :: shared_ptr在关机时执行任意清理。起初,我认为此代码可能无法工作,但随后尝试了以下操作:#include <memory>#include <iostream>#include <vector>class test {public:  test() {    std::cout << "Test created" << std::endl;  }  ~test() {    std::cout << "Test destroyed" << std::endl;  }};int main() {  std::cout << "At begin of main.\ncreating std::vector<std::shared_ptr<void>>"             << std::endl;  std::vector<std::shared_ptr<void>> v;  {    std::cout << "Creating test" << std::endl;    v.push_back( std::shared_ptr<test>( new test() ) );    std::cout << "Leaving scope" << std::endl;  }  std::cout << "Leaving main" << std::endl;  return 0;}该程序给出输出:At begin of main.creating std::vector<std::shared_ptr<void>>Creating testTest createdLeaving scopeLeaving mainTest destroyed我有一些关于为什么可行的想法,这与为G ++实现的std :: shared_ptrs的内部有关。由于这些对象将内部指针与计数器包装在一起,因此从std::shared_ptr<test>到std::shared_ptr<void>的转换可能不会妨碍析构函数的调用。这个假设正确吗?当然还有一个更重要的问题:这是否可以保证按标准运行,或者可能进一步更改std :: shared_ptr的内部结构,其他实现实际上会破坏此代码吗?
查看完整描述

3 回答

?
不负相思意

TA贡献1777条经验 获得超10个赞

诀窍是std::shared_ptr执行类型擦除。基本上,shared_ptr创建新deleter函数时,它将在内部存储一个函数(可以将其作为构造函数的参数提供,但如果不存在,则默认为call delete)。当shared_ptr销毁时,它将调用该存储的函数,然后将调用deleter。


可以使用std :: function简化类型擦除的简单草图,并避免所有引用计数和其他问题在此处显示:


template <typename T>

void delete_deleter( void * p ) {

   delete static_cast<T*>(p);

}


template <typename T>

class my_unique_ptr {

  std::function< void (void*) > deleter;

  T * p;

  template <typename U>

  my_unique_ptr( U * p, std::function< void(void*) > deleter = &delete_deleter<U> ) 

     : p(p), deleter(deleter) 

  {}

  ~my_unique_ptr() {

     deleter( p );   

  }

};


int main() {

   my_unique_ptr<void> p( new double ); // deleter == &delete_deleter<double>

}

// ~my_unique_ptr calls delete_deleter<double>(p)

当shared_ptr从另一个复制(或默认构造)一个a时,删除器会被传递,因此当您shared_ptr<T>从构造a 时shared_ptr<U>,有关要调用的析构函数的信息也会被传递deleter。


查看完整回答
反对 回复 2019-11-25
?
凤凰求蛊

TA贡献1825条经验 获得超4个赞

shared_ptr<T> 逻辑上[*]具有(至少)两个相关的数据成员:


指向被管理对象的指针

指向将用于销毁它的删除函数的指针。

您的删除器功能shared_ptr<Test>给你构建的方式,是正常的一个Test,该指针转换为Test*和delete这样。


当您将推shared_ptr<Test>入的向量时,尽管第一个转换为shared_ptr<void>,这两个都将被复制void*。


因此,当向量元素在使用最后一个引用被破坏时,它将指针传递给正确删除它的删除器。


实际上,这要复杂一些,因为shared_ptr可以使用删除器函子而不是函数,因此甚至可能存储每个对象的数据,而不仅仅是函数指针。但是对于这种情况,没有这样的额外数据,仅存储指向模板函数实例化的指针就足够了,该模板参数具有捕获必须删除指针的类型的模板参数。


[*]从逻辑上讲,它可以访问它们-它们可能不是shared_ptr本身的成员,而是它指向的某些管理节点。


查看完整回答
反对 回复 2019-11-25
?
紫衣仙女

TA贡献1839条经验 获得超15个赞

shared_ptr<T>(Y *p)实际上,构造函数似乎正在调用shared_ptr<T>(Y *p, D d)where d是为对象自动生成的删除器。


发生这种情况时,对象的类型Y是已知的,因此该shared_ptr对象的删除器知道要调用哪个析构函数,并且当指针存储在向量中时,此信息也不会丢失shared_ptr<void>。


的确,规范要求接收shared_ptr<T>对象接受shared_ptr<U>对象必须为true并且U*必须隐式转换为a T*,这确实是这种情况,T=void因为任何指针都可以void*隐式转换为指针。没有任何关于删除器的说明,因为删除器将是无效的,因此确实规范要求它可以正常工作。


从技术上讲,IIRC a shared_ptr<T>包含指向包含参考计数器的隐藏对象的指针和指向实际对象的指针;通过将删除器存储在此隐藏结构中,可以使此看似魔术的功能正常工作,同时仍保持shared_ptr<T>与常规指针一样大的大小(但是,取消引用指针需要双重定向)


shared_ptr -> hidden_refcounted_object -> real_object


查看完整回答
反对 回复 2019-11-25
  • 3 回答
  • 0 关注
  • 1282 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信