动态对象分配器
SmartESQL 引擎通过将类为分配器的对象作为参数来管理诸如 Blob、String、Array 等动态数据对象,以便使用由分配器管理的内存来构建(并复制)动态对象。SmartESQL 引擎提供了三种类型的分配器:
会话分配器:
SqlEngine::getAllocator()
或McoSqlSession::getAllocator()
- 覆盖会话(应用程序线程)的生命周期。预编译语句分配器:
PreparedStatement::getAllocator()
- 覆盖预编译语句的生命周期。结果集分配器:
QueryResult::allocator
- 覆盖结果集的生命周期。
开发人员可以通过 C++ 动态内存分配或在栈上手动创建 Allocator 类的对象,并使用它来构造 SQL 动态对象。
由任何分配器创建的动态对象都可以在任何时候通过调用以下方法显式删除。
DELETE_OBJ(Allocator *allocator, DynamicObject *object);
所有未被显式删除的动态对象只要其分配器对象还存在就一直有效。并且当分配器被销毁时,所有这些对象都会自动被删除。
以下是一个带有说明性注释的代码片段,展示了分配器的用法:
{
SqlEngine engine;
... // 初始化代码
{
QueryResult result( engine->executeQuery(query));
Cursor* iterator = result->records();
while ( iterator->hasNext() )
{
// 本地创建的分配器对象
Allocator on_stack_variable_allocator;
Record* rec = iterator->next();
// 获取结果集第 0 列的字符串值
// 使用本地创建的分配器创建对象“on_stack_alloc_string”
String* on_stack_alloc_string = rec->get(0)
->stringValue(&on_stack_variable_allocator);
// 使用结果集分配器创建对象“result_set_alloc_string”。
// 实际是由封装类QueryResult返回的结果集字段ResultSet::allocator。
String* result_set_alloc_string = rec->get(0)
->stringValue(result->allocator);
// 使用会话分配器创建对象“session_alloc_string”。
// 请注意,此分配器对象不可重入。
// 因此,在不同的线程中,必须使用特定的McoSqlSession类对象的方法getAllocator。
String* session_alloc_string = rec->get(0)
->stringValue(engine.getAllocator());
...
// 在此处使用字符串对象
// 显式删除对象 on_stack_alloc_string(否则其内存将在 1 处释放)
DELETE_OBJ(&on_stack_variable_allocator, on_stack_alloc_string);
// 显式删除对象 result_set_alloc_string(否则其内存将在第 2 处释放)
DELETE_OBJ(result->allocator, result_set_alloc_string);
// 显式删除对象 session_alloc_string(否则其内存将在第 3 处释放)
DELETE_OBJ(engine.getAllocator(), session_alloc_string);
} // 1处:本地分配器“on_stack_variable_allocator”被销毁,
// 所分配的所有剩余对象也会被删除。
} // 2处:此处结果集分配器“result->allocator”被销毁,
// 其所有剩余已分配的对象也会被删除。
... // 更多代码
} // 3处:这里会销毁会话分配器“engine.getAllocator()”,
//并且它剩余的所有已分配对象也会被删除。
ValueRef和String类
若要处理动态对象,可以使用模板类 ValueRef。此辅助类会自动使用原始对象的分配器来构造一个副本。
例如:
QueryResult result(engine.executeQuery(query));
Cursor* iterator = result->records();
while ( iterator->hasNext() )
{
Record* rec = iterator->next();
ValueRef nameRef(rec->get(0));
String * pName = nameRef.as<String>();
// 对象 pName 是使用结果集分配器对象构建的。
}
所有从基类 Value 继承的类都实现了一个方法 String *stringValue()
。在该方法的所有实现中(String 类除外),都会创建一个新的 *String
类型的对象,并且在使用完毕后应当将其删除。而 String 类的方法则返回其自身的“this
”指针。
因此,如果应用程序删除了String对象,然后又删除了当前对象所继承的对象,那么该对象就会被删除两次!而且一旦删除,也无法再使用父对象。
为避免混淆,建议不要使用 String *stringValue()
方法将 Value 转换为字符串,而是使用 Ref<String> stringRef(Allocator* allocator)
方法,该方法会正确处理删除操作。使用该方法有两种方式:
Ref<String> strval(v->stringRef(allocator));
Ref<String> str = v->stringRef(allocator);
引用的作用域和用法说明如下:
{
Ref<String> strval(v->stringRef(allocator));
/* 将引用作为本地对象字符串使用 */
printf("%s", strval.cstr());
} /* 此处“Ref”会自动删除。 */
或
{
Ref<String> str = v->stringRef(allocator);
/* 将引用用作指向 String 对象的指针 */
printf("%s", str->cstr());
} /* 此处“Ref”会自动删除。 */