D语言的RAII支持

RAII(Resource Acquisition Is Initialization 资源获得即初始化)是管理资源的一种方式,它在构造对象时初始化资源,析构对象时释放资源,有时也把这2个过程分为RAII和RRID(Resource Release Is Destruction 资源释放即析构),这通常需要语言支持。

大部分语言都支持RAII,RRID则有少数语言不支持,这些语言无法在域结束时自动销毁栈上分配的对象,java就是这种。

先来看看一个简单的JDBC调用过程:

Connection conn = null;
Statement stmt = null;
ResultSet rset = null;
try{
conn = createConnection();
stmt = conn.prepareStatement("SELECT * FROM USERS");
rset = stmt.executeQuery();
// 使用rset ...
}finally{
if(rset!=null)rset.close();
if(stmt!=null)stmt.close();
if(conn!=null)conn.close();
}

C++中在栈上分配的对象离开它的作用域时会自动调用析构函数,类似功能的代码大概只要写成这样就可以了:
[code]
conn = createConnection();
stmt = conn.prepareStatement("SELECT * FROM USERS");
rset = stmt.executeQuery();
// 使用rset ...
[/code]
如果析构函数中调用了close,它会在这几个变量离开作用域时按它们声明的相反顺序调用析构函数,也就达到了按这个顺序进行close的效果。

考虑到上面的Connection/Statement/ResultSet都是接口,如果在C++中也来这样操作接口的话,同样无法享受这种便利,因为在C++中用接口来操作意味着你必须使用指针,不幸的是指针没有析构函数,它只是简单类型。不过我们可以把这个工作委托给另一个对象,由这个对象来调用析构函数,你可以使用auto_ptr。或者可以参考ScopeGuard(由Andrei Alexandrescu & Petru Marginean 实现),它可以调用一个指定的方法,而不限于析构一个对象。C++能做到这些,因为C++栈上分配的对象在离开作用域时会自动析构,即便是发生异常也风雨无阻,别在析构函数中再抛异常就行了。

auto_ptr版本:
[code]
auto_ptr<Connection> conn (createConnection());
auto_ptr<Statement> stmt (conn->prepareStatement("SELECT * FROM USERS"))
auto_ptr<ResultSet> rset (stmt->executeQuery());
// 使用rset ...
[/code]
ScopeGuard版本:
[code]
template <class T>
void ReleaseObject(T* obj){
delete (T*)obj;
}
Connection* conn = createConnection();
ScopeGuard connGuard(ReleaseObject<Connection>, conn);
Statement* stmt = conn->prepareStatement("SELECT * FROM USERS");
ScopeGuard connGuard(ReleaseObject<Statement>, stmt);
ResultSet* rset = stmt->executeQuery();
ScopeGuard connGuard(ReleaseObject<ResultSet>, rset);
// 使用rset ...
[/code]
ScopeGuard使用起来稍麻烦,原因是它不是为了析构对象而设计的,它是为了在自己析构时,执行一个指定任务,用起来很灵活。

C#使用using来达到相同的功能,不熟悉也不用它,就不去找代码了。

如果用ruby来做,我想可能是这样吧:

createConnection do |conn|
conn.preparedStatement("SELECT * FROM USERS") do |stmt|
rset = stmt.executeQuery();
# 使用 rset ...
end
end

由于代码都在块中,只需要在调用块的前后初始化及释放就可以了,从这点上来说,java也可以这样做,代码丑了点而已。。。

最后看看D语言是怎么做的吧:
[code]
scope Connection conn = createConnection();
scope Statement stmt = conn.prepareStatement("SELECT * FROM USERS");
scope ResultSet rset = stmt.executeQuery();
// 使用 rset ...
[/code]
上面的Connection/Statement/ResultSet都可以是接口,作为“C++的改良者”,看起来至少在语法上它有所改进。可以看到它所用的代码不如C++/java这么多,也不需要C#和ruby这样构造出一“块”代码。

[注:上面用的scope关键字,原来是用auto,0.174中似乎说RAII中使用的auto应该被scope代替。]

D语言还从语言层次上提供了scope guard:
[code]
Connection conn = createConnection();
scope(exit) conn.close();
Statement stmt = conn.prepareStatement("SELECT * FROM USERS");
scope(exit) stmt.close();
ResultSet rset = stmt.executeQuery();
scope(exit) rset.close();
// 使用 rset
[/code]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值