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

我发现一些代码使用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 test

Test created

Leaving scope

Leaving main

Test destroyed

我有一些关于为什么可行的想法,这与为G ++实现的std :: shared_ptrs的内部有关。由于这些对象将内部指针与计数器包装在一起,因此从std::shared_ptr<test>到std::shared_ptr<void>的转换可能不会妨碍析构函数的调用。这个假设正确吗?


当然还有一个更重要的问题:这是否可以保证按标准运行,或者可能进一步更改std :: shared_ptr的内部结构,其他实现实际上会破坏此代码吗?


一只萌萌小番薯
浏览 1283回答 3
3回答

不负相思意

诀窍是std::shared_ptr执行类型擦除。基本上,shared_ptr创建新deleter函数时,它将在内部存储一个函数(可以将其作为构造函数的参数提供,但如果不存在,则默认为call delete)。当shared_ptr销毁时,它将调用该存储的函数,然后将调用deleter。可以使用std :: function简化类型擦除的简单草图,并避免所有引用计数和其他问题在此处显示:template <typename T>void delete_deleter( void * p ) {&nbsp; &nbsp;delete static_cast<T*>(p);}template <typename T>class my_unique_ptr {&nbsp; std::function< void (void*) > deleter;&nbsp; T * p;&nbsp; template <typename U>&nbsp; my_unique_ptr( U * p, std::function< void(void*) > deleter = &delete_deleter<U> )&nbsp;&nbsp; &nbsp; &nbsp;: p(p), deleter(deleter)&nbsp;&nbsp; {}&nbsp; ~my_unique_ptr() {&nbsp; &nbsp; &nbsp;deleter( p );&nbsp; &nbsp;&nbsp; }};int main() {&nbsp; &nbsp;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。

凤凰求蛊

shared_ptr<T> 逻辑上[*]具有(至少)两个相关的数据成员:指向被管理对象的指针指向将用于销毁它的删除函数的指针。您的删除器功能shared_ptr<Test>给你构建的方式,是正常的一个Test,该指针转换为Test*和delete这样。当您将推shared_ptr<Test>入的向量时,尽管第一个转换为shared_ptr<void>,这两个都将被复制void*。因此,当向量元素在使用最后一个引用被破坏时,它将指针传递给正确删除它的删除器。实际上,这要复杂一些,因为shared_ptr可以使用删除器函子而不是函数,因此甚至可能存储每个对象的数据,而不仅仅是函数指针。但是对于这种情况,没有这样的额外数据,仅存储指向模板函数实例化的指针就足够了,该模板参数具有捕获必须删除指针的类型的模板参数。[*]从逻辑上讲,它可以访问它们-它们可能不是shared_ptr本身的成员,而是它指向的某些管理节点。

紫衣仙女

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
打开App,查看更多内容
随时随地看视频慕课网APP