C++没有GC机制,当C++对象被导出到JavaScript环境后,必须使用某种方法进行对象生命周期管理,以彻底杜绝野指针、内存泄漏,引用计数无疑是最常用的方法。
对象生命周期管理需要解决的问题是:当一个对象可能在多个地方被引用时,如何决定何时将其销毁。引用计数法解决这一问题的途径非常简单:
C++中一般通过在基类中添加AddRef()
和Release()
成员函数来实现引用计数的增减,例如:
#include <atomic>
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
#endif
class CRefCount {
public:
CRefCount() : m_ref_count(1) {}
virtual ~CRefCount() {}
void AddRef() {
m_ref_count++;
}
int Release() {
int t = --m_ref_count;
if (t == 0) delete this;
return t;
}
protected:
std::atomic<int> m_ref_count;
};
CRefCount
构造时其引用计数成员m_ref_count
设为1,因为当我们创建CRefCount
的实例时立即获得了该对象的引用(指针),此时引用计数就应为1。计数成员m_ref_count
为std::atomic<int>
型,该类型可以保证在多线程条件下的原子操作,避免并发读写错误。
AddRef()
/Release()
使用规则AddRef()
/Release()
的使用一般遵循以下规则:
AddRef()
;当一个内存位置所指向的内存引用不再使用时,应该调用Release()
并将该内存位置设为null
;Release()
,以通知A对象不再被使用,然后再调用B对象的AddRef()
;AddRef()
/Release()
。第1条与第2条相对好理解,比较麻烦的是第3条,例如:
CRefCount* obj = new CRefCount();
obj->AddRef();
//do sth. with obj:
Func(obj);
obj->Release();
SAFE_RELEASE(obj);
围绕Func(obj)
上下的AddRef()
和Release()
事实上是不必要的,因为obj
在执行Func(obj)
的过程中始终是有效的;再比如:
void Func(CRefCount* obj) {
if (!obj) return;
obj->AddRef();
CRefCount* temp = obj;
//do sth. with temp:
//...
SAFE_RELEASE(temp);
}
obj->AddRef()
和temp->Release()
也是无必要的,因为局部变量temp
的生命周期与Func()
函数一致,已被包含在obj
的生命周期中。
引用计数增减规则可以简化为:
AddRef()
,否则可不调用;AddRef()
;Release()
,更改后AddRef()
;AddRef()
/Release()
。第1~3条分别对应以下例子:
第1条:
void Func(CRefCount* obj) {
//do sth. with obj:
//...
}
第2条:
CRefCount* g_obj = new CRefCount();
CRefCount* GetGlobalObj() {
g_obj->AddRef();
return g_obj;
}
第3条:
CRefCount* g_obj = new CRefCount();
void UpdateObj(CRefCount*& obj) {
SAFE_RELEASE(obj);
g_obj->AddRef();
obj = g_obj;
}
AddRef()
/Release()
根据4.3节的介绍,如果所有的C++对象都继承自同一个CRefCount
基类,那么只需要导出CRefCount
的AddRef()
/Release()
即可,无需单独为每个子类导出引用计数增减函数。例如:
//ref_count.cpp
#include <stdio.h>
#include <atomic>
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
#endif
class CRefCount {
public:
CRefCount() : m_ref_count(1) {}
virtual ~CRefCount() { printf("CRefCount:~CRefCount()\n"); }
void AddRef() {
m_ref_count++;
}
int Release() {
int t = --m_ref_count;
printf("refcount now:%d\n", t);
if (t == 0) delete this;
return t;
}
protected:
std::atomic<int> m_ref_count;
};
struct RefCount;
EM_PORT_API(void) CObj_AddRef(struct RefCount* obj) {
CRefCount *ro = (CRefCount*)obj;
ro->AddRef();
}
EM_PORT_API(int) CObj_Release(struct RefCount* obj) {
CRefCount *ro = (CRefCount*)obj;
if (ro) {
return ro->Release();
}
else return 0;
}
//-----------------------------------
class CShape : public CRefCount{
public:
CShape() {}
virtual ~CShape() { printf("CShape:~CShape()\n"); }
virtual void WhatAreYou() = 0;
};
struct Shape;
EM_PORT_API(void) Shape_WhatAreYou(struct Shape* shape) {
CShape *obj = (CShape*)shape;
obj->WhatAreYou();
}
//-----------------------------------
class CTriangle : public CShape {
public:
CTriangle() {}
virtual ~CTriangle() { printf("CTriangle:~CTriangle()\n"); }
void WhatAreYou(){ printf("I'm a triangle.\n"); }
};
EM_PORT_API(struct Shape*) Shape_Create_Triangle() {
CTriangle *obj = new CTriangle();
return (struct Shape*)obj;
}
JavaScript代码如下:
//ref_count.html
Module = {};
var g_t = 0;
Module.onRuntimeInitialized = function() {
var t = Module._Shape_Create_Triangle();
Module._Shape_WhatAreYou(t);
Module._CObj_AddRef(t);
g_t = t;
Module._CObj_Release(t);
t = 0;
setTimeout("Module._CObj_Release(g_t);g_t = 0", 2000);
}
浏览页面,过2秒后控制台输出如下:
至此,我们完成了C++对象的导出及生命周期控制。