[WebKit]RefPtr和PassRefPtr基础[1]

本文探讨了WebKit项目中使用的智能指针模式,包括RefPtr和PassRefPtr的使用方式,以及它们如何帮助解决内存泄漏问题。文章还介绍了如何在函数参数和返回值中正确使用这两种智能指针。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

历史:

     在WebKit中,许多对象采用了引用计数。这种模式是通过类的ref,deref成员函数来递增和递减对象的引用记数。调用一次ref必须调用一次deref。当对象的引用记数为0的时候,对象就被删除。WebKit中许多类创建的新对象引用记数都为0,这被称作是浮动状态(Floating State)。在浮动状态的对象必须调用ref,在删除之前必须调用deref。WebCore中许多类通过继承RefCounted模版类来实现这种模式。
在2005年的时候,我们发现存在很多内存泄漏的问题,特别实在WebCore编辑器代码中,这主要是由没有正确的使用ref和deref调用,还有就是创建的对象没有调用ref,依然是浮动状态。
     我们决定我们使用智能指针来解决这个问题,然而,一些前期的实验表明,智能指针导致引用记数的其他操纵影响性能。例如,一个函数使用智能指针来传递参数,函数返回时也使用这个智能指针作为返回值,仅仅在一个对象从一个智能指针移动到另外一个时,传递参数和返回函数值时就递增和递减引用记数2-4次。因此,我们寻求一种能够让我们使用智能指针又避免使用这种引用记数的性能流失的方法。
这种解决方案的灵感来源于C++的标准模版类auto_ptr。应用这种模式的对象在赋值的时候将传递了所有权。当你把一个auto_ptr传递给另外一个时,传递者变为0。
      Maciej Stachowiak设计了一对模版类,RefPtr和PassRefPtr,它为WebCore的引用记数实现了这种模式。
原始指针:

      在讨论如RefPtr模版类这类智能指针时,我们使用原始指针来构建,下面是使用原始指针写的规范的Setter函数。

RefPtr
RefPtr是一种简单的智能指针,主要是通过在传入值时调用ref,传出时调用deref。RefPtr工作在有ref和deref成员函数的对象上。下面是一个例子:

单独使用RefPtr会导致引用记数的流失,例如下面的例子:

从这个方面来说,假定Node对象开始时引用记数为0,当它被赋值到a时,递增引用记数为1。在创建返回值时递增引用记数到2,当a销毁的时候递减回1.然后在创建b的时候引用记数递增到2,在createSpecialNode函数的返回值销毁时递减到1.(这些分析忽略了编译器返回值优化的可能性,如果编译器这么做了,可能导致引用记数的流失)
当涉及到函数参数和返回值时,引用记数的流失的代价比较大,解决方法就是PassRefPtr。

PassRefPtr
PassRefPtr除过有一点区别其他都和RefPtr类似,当传递一个PassRefPtr,或者把PassRefPtr赋值到RefPtr或者另一PassRefPtr时,原始的指针值设置为0;操作没有做任何对引用记数的更改。下面看一个新的例子:

Node对象开始时,引用记数为0,当被赋值到a时,引用记数加1,在返回值PassRefPtr创建时,a被设置为0,当创建b时,返回值设置为0。
然后,当我们开始使用PassRefPtr编码时,Safari团队发现当被赋值到另一个变量时,指针变为了0,这种很容易导致错误。

在wear被调用时,ring已经是空指针了,为了避免这种情况,建议PassRefPtr只在作为函数参数,返回值和拷贝参数到RefPtr局部变量时使用。

混合使用RefPtr和PassRefPtr

除过当传递参数或者函数返回值时这些情况外,建议使用RefPtr。,在打算把RefPtr所有权转移到PassRefPtr时,RefPtr类有一个release成员函数,它能够设置RefPtr到0,然后构建一个PassRefPtr对象,这期间没有改变引用记数。

这种方式保持了PassRefPtr的有效性,同时也降低了导致问题的可能性。

与原始指针混合使用

RefPtr使用get方法来获得一个原始指针

然而,这些操作可以不使用get调用,而通过RefPtr和PassRefPtr直接完成。

 

[参考]http://webkit.org/coding/RefPtr.html

### 解析 C++ 中 `int**` `int*&` #### 定义区别 - **双重指针 (`int**`)** 双重指针是指向指针的指针。这意味着它存储的是另一个指针变量的地址,而该另一指针又指向一个整数类型的对象。 - **指针引用 (`int* &`)** 指针引用是一个引用类型,绑定到某个特定的指针上。这使得通过这个引用可以修改原始指针所指向的内容或改变其本身指向的位置[^1]。 #### 使用场景差异 当声明为 `int** a` 时,表示创建了一个新的指针变量来保存其他指针的地址;而对于 `int* &a` 来说,则是对现有指针的一个别名或者说是一种间接访问方式,允许直接操作原指针而不必担心额外分配空间给新变量。 下面给出具体的例子以便更清晰地展示两者的不同之处: ```cpp #include <iostream> using namespace std; void modifyDoublePointer(int** ptr){ *ptr = new int(9); // 修改实际内存位置中的值 } void modifyReferenceToPointer(int*& refPtr){ refPtr = new int(7); // 改变refPtr本身的指向并更新其所指内容 } int main(){ int num = 0; { int* singlePtr = nullptr; modifyDoublePointer(&singlePtr); cout << "*singlePtr after modification via double pointer: " << *singlePtr << endl; // 输出应为9 delete singlePtr; // 清理动态分配的空间 } { int* anotherSinglePtr = nullptr; modifyReferenceToPointer(anotherSinglePtr); cout << "*anotherSinglePtr after modification via reference to pointer: " << *anotherSinglePtr << endl; // 输出也应该是7 delete anotherSinglePtr; // 同样清理资源 } return 0; } ``` 在这个程序里可以看到,无论是通过传递双重指针还是指针引用的方式都可以实现对外部定义好的单层指针的操作,但是它们内部的工作机制完全不同。前者需要传入目标指针的地址从而获得对其内容写权限,后者则是作为原有实体的新名字来进行交互。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值