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