在查看interFoam
求解器代码过程中,在alphaEqn.H
头文件中发现了如下命令:
tmp<fv::ddtScheme<scalar>> ddtAlpha
(
fv::ddtScheme<scalar>::New
(
mesh,
mesh.ddtScheme("ddt(alpha)")
)
);
这里,我们对其中的tmp
做具体探讨。
1.介绍
tmp
是一个包装类,它允许从functions / methods
方法返回本地对象,而无需复制。它还允许程序快速清除内存中的对象,这可以用来减少峰值内存。tmp
在后台工作,通常在顶层代码中不可见。
它并不是必须的,但可以减少程序运行过程中的工作量并提高程序的效率。
在OpenFOAM中,我们一般用它来处理场量数据(场量存储数据一般很大,传统的数据传递方式浪费内存资源,甚至超出允许范围)。我们可以在返回场量对象时显式地使用它。此外,tmp
可以应用于算法中间,删除场量空间,以减少峰值内存。
单个字段以上的所有对象都可以使用tmp
。它们都可以从tmp<object>
转换为object
。要使用tmp返回操作对象,只需将返回类型用tmp<>
括起来:
2.代码分析
//原始数据
xxxField myFunc ()
{
xxxField returnMe(new xxxField);
// 返回局部变量
return returnMe;
}
// 处理后
tmp<xxxField> myFunc()
{
tmp<xxxField> tReturnMe(new xxxField);
xxxField& returnMe = tReturnMe();//()表示引用
// 返回局部变量
return tReturnMe;
}
清除场量所占用的空间,需在tmp
中应用discardable data
tmp<xxxField> discardableDataObject;
或者
discardableDataObject.clear();
可以查看tmpI.H
里面的代码,里面有关于clear()
函数的声明。求解器里面也有类似的使用,如:UEqn.clear()
;
3.总结
(该部分翻译自http://openfoamwiki.net/index.php/OpenFOAM_guide/tmp)
正常情况下,编译器要先调用复制构造函数,创建复制的返回文件,然后再调用析构函数清除原来的对象。而tmp
对象取代了这个两个步骤,当编译器调用复制构造函数时,tmp
仅仅复制指针和引用,而数据原封不动,当编译器调用析构函数时,tmp
将他的指针重定向到NULL
。因为数据有了一个新的引用,编译器也就不管他了,数据也就保留了下来。
重写复制构造函数和析构函数不足以使tmp
工作。如果tmp
要透明,它的行为必须与展开对象的正常行为相同。这意味着有时需要正确地删除tmp
,或者需要正确地复制tmp
的数据。这意味着它需要被检测,判断它是一个临时的局部对象,或是一个重复的对象,还是堆栈上的常规对象。为此:
tmp
持有一个标志来确定它是否是临时对象。
- 如果
tmp
是由指向对象的指针构造的,则该标志为true
,就像使用new
关键字一样。 - 如果
tmp
是由引用构造的,则标志设置为false
,如果对象已经存在,或者tmp
被推送到堆 栈上,就会发生这种情况。 - 有关适用于指针的类似包装类,请参见
autoPtr
。这一部分可参考苏老师的博客
tmp
所指向的对象需要实时跟踪有多少引用指向它们。
- 这是通过
refCount
类实现的,也是实现tmp
的高速缓存之一。
当被删除时,tmp
询问它的对象有多少引用指向它。
- 如果有多个指针,
tmp
会在删除时重定向其指针。 - 如果只有一个指针,
tmp
不会重定向指针,数据最终会被删除。
参考文献:
[1.]:http://openfoamwiki.net/index.php/OpenFOAM_guide/tmp
[2]:http://blog.sina.com.cn/s/blog_5fdfa7e60100rdb3.html