C++中在堆中申请的内存都需要程序员自己手动删除,这是C++容易造成内存泄漏的根本原因。使用过Java的朋友都清楚,Java有完善的内存回收机制,无需程序员调用释放内存的操作。C++里是否能够实现类似Java的自动回收内存的机制呢?答案是肯定的,而Webrtc中的scoped_refptr就是实现类似的功能。
首先我们从这个类的名字入手去理解它的含义。名字由三个单词组成,分别为scope,ref,ptr。
先看ptr,C++程序员都知道这是pointer指针的意思,但是类名中包含指针的语义,这个该如何理解呢?笔者不才,说实话,这个我也是想了好久才弄明白是怎么回事。先说结论吧,在webrtc中,凡是以ptr结尾的类,若以此类创建的非指针对象,均可以当指针对象的语法使用。估计读者有点蒙,下面以实际例子说明。
一般来说,C++中有两种创建对象的方法,一种是创建指针对象。
-
A *a = new A();
-
a->func();
另一种是创建非指针对象:
-
A a;
-
a.func()
这两种方法创建的对象,对象调用方法的语法是不一样的。一个是以->方式调用,一个是以.方式调用。但是请看:
-
scoped_refptr<A> temp;
-
temp->func();
注意到了吗?temp是以非指针对象的方式创建的,但是它可以使用指针对象的语法调用函数方法。这个是如何实现的呢?这个其实一点也不难,scoped_refptr的代码中实现了operator->的操作符。
T* operator->() const { return ptr_; }
因此,像这种以ptr结尾的类,最好都以非指针的方式来创建对象。以非指针方式创建对象还有另一个好处,就是当对象离开它所属的作用域时,会自动调用对象的析构函数进行自行销毁。这个也跟接下来要讲的scope单词相关。
要想弄明白scope的含义,我们要学一个概念,叫RAII。这个概念如果单纯去看这个英文的全名(Resouce Acquisition Is Initialization),会让人莫名其妙,一头雾水。其实就是把一个指向堆内存空间的指针,存放在一个栈空间对象中,通过栈空间对象自动回收的功能,同时回收此指针指向的堆内存的方法。看个简单的例子吧,如果我们在一段作用域代码中创建了一个指针对象,当离开此作用域时,如果该指针没有传递给别的方法时,此时应该显式调用delete对指针进行释放
1.A* a = new A();
2.delete a;
3.a = NULL;
虽然对于经验丰富的C++程序员来说,忘记写最后两句的概率并不大,但是这些大量的释放内存的代码嵌在业务代码中也不能忽视。因此就有些人想了个高级的办法把指针的释放封装了起来。做法如下:
class scoped_A{undefined
private :
A *a;
public:
scoped_A(A* a){undefined
this.a = a;
}
~scoped_A(){undefined
delete a;
a = NULL;
}
}
具体用法:
-
A* a = new A()
-
scoped_A(a) sa;
或者:
scoped_A sa(new A());
看到了吗?你可以理解为使用一个scoped_A的对象sa把指针对象a包住,当sa离开作用域自动调用scoped_A的析构函数,以达到自动释放a指针的目的。不得不说,这个是很妙的做法。这也是scope的含义,是指这个ptr对象是有范围的,离开了作用域,会自动回收。
最后来说说ref,ref是引用的意思。在JAVA中,我们经常说一块内存会被多个变量引用,当这块内存没有被任何变量引用的时候,内存将会被垃圾回收机制所回收。在C++中,堆中的一块内存也会被多个指针变量同时引用(指向),有没有办法也像JAVA一样,在这块堆内存没有被任何指针引用时,自动回收呢?这个就要说到Webrtc中的RefCountedObject类了。关于这个类的解释可以看另一篇文章。要想彻底弄懂scoped_refptr,需要两篇文章放在一起看。
总结:scoped_refptr是一个实现了在C++上自动回收对象内存的功能类。它能够在对象离开作用域时,通过引用计数器来判断是否回收堆中内存,实现了类似Java的垃圾回收的机制。